/src/net-snmp/snmplib/transports/snmpAAL5PVCDomain.c
Line | Count | Source |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | |
3 | | #include <net-snmp/library/snmpAAL5PVCDomain.h> |
4 | | |
5 | | #include <stdio.h> |
6 | | #include <sys/types.h> |
7 | | #include <ctype.h> |
8 | | #include <errno.h> |
9 | | |
10 | | #ifdef HAVE_STRING_H |
11 | | #include <string.h> |
12 | | #else |
13 | | #include <strings.h> |
14 | | #endif |
15 | | #ifdef HAVE_STDLIB_H |
16 | | #include <stdlib.h> |
17 | | #endif |
18 | | #ifdef HAVE_UNISTD_H |
19 | | #include <unistd.h> |
20 | | #endif |
21 | | #ifdef HAVE_SYS_SOCKET_H |
22 | | #include <sys/socket.h> |
23 | | #endif |
24 | | #include <atm.h> |
25 | | |
26 | | #include <net-snmp/types.h> |
27 | | #include <net-snmp/output_api.h> |
28 | | #include <net-snmp/config_api.h> |
29 | | |
30 | | #include <net-snmp/library/snmp.h> |
31 | | #include <net-snmp/library/snmp_transport.h> |
32 | | #include <net-snmp/library/tools.h> |
33 | | |
34 | | |
35 | | const oid netsnmp_AAL5PVCDomain[10] = { NETSNMP_ENTERPRISE_MIB, 3, 3, 3 }; |
36 | | static netsnmp_tdomain aal5pvcDomain; |
37 | | |
38 | | |
39 | | /* |
40 | | * Return a string representing the address in data, or else the "far end" |
41 | | * address if data is NULL. |
42 | | */ |
43 | | |
44 | | static char * |
45 | | netsnmp_aal5pvc_fmtaddr(netsnmp_transport *t, const void *data, int len) |
46 | 0 | { |
47 | 0 | const struct sockaddr_atmpvc *to = NULL; |
48 | |
|
49 | 0 | if (data != NULL && len == sizeof(struct sockaddr_atmpvc)) { |
50 | 0 | to = (const struct sockaddr_atmpvc *) data; |
51 | 0 | } else if (t != NULL && t->data != NULL && |
52 | 0 | t->data_length == sizeof(struct sockaddr_atmpvc)) { |
53 | 0 | to = (const struct sockaddr_atmpvc *) t->data; |
54 | 0 | } |
55 | 0 | if (to == NULL) { |
56 | 0 | return strdup("AAL5 PVC: unknown"); |
57 | 0 | } else { |
58 | 0 | char *tmp; |
59 | |
|
60 | 0 | if (asprintf(&tmp, "AAL5 PVC: %hd.%hd.%d", to->sap_addr.itf, |
61 | 0 | to->sap_addr.vpi, to->sap_addr.vci) < 0) |
62 | 0 | tmp = NULL; |
63 | 0 | return tmp; |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | static void |
68 | | netsnmp_aal5pvc_get_taddr(netsnmp_transport *t, void **addr, size_t *addr_len) |
69 | 0 | { |
70 | 0 | struct sockaddr_atmpvc *sa = t->remote; |
71 | 0 | unsigned char *p; |
72 | |
|
73 | 0 | *addr_len = 8; |
74 | 0 | if (!(*addr = malloc(*addr_len))) |
75 | 0 | return; |
76 | 0 | p = *addr; |
77 | 0 | p[0] = sa->sap_addr.itf >> 8; |
78 | 0 | p[1] = sa->sap_addr.itf >> 0; |
79 | 0 | p[2] = sa->sap_addr.vpi >> 8; |
80 | 0 | p[3] = sa->sap_addr.vpi >> 0; |
81 | 0 | p[4] = sa->sap_addr.vci >> 24; |
82 | 0 | p[5] = sa->sap_addr.vci >> 16; |
83 | 0 | p[6] = sa->sap_addr.vci >> 8; |
84 | 0 | p[7] = sa->sap_addr.vci >> 0; |
85 | 0 | } |
86 | | |
87 | | /* |
88 | | * You can write something into opaque that will subsequently get passed back |
89 | | * to your send function if you like. For instance, you might want to |
90 | | * remember where a PDU came from, so that you can send a reply there... |
91 | | */ |
92 | | |
93 | | static int |
94 | | netsnmp_aal5pvc_recv(netsnmp_transport *t, void *buf, int size, |
95 | | void **opaque, int *olength) |
96 | 0 | { |
97 | 0 | int rc = -1; |
98 | |
|
99 | 0 | if (t && NETSNMP_IS_VALID_SOCKET(t->sock)) { |
100 | 0 | while (rc < 0) { |
101 | 0 | rc = recvfrom(t->sock, buf, size, 0, NULL, NULL); |
102 | 0 | if (rc < 0 && errno != EINTR) { |
103 | 0 | break; |
104 | 0 | } |
105 | 0 | } |
106 | |
|
107 | 0 | if (rc >= 0) { |
108 | 0 | DEBUGIF("netsnmp_aal5pvc") { |
109 | 0 | char *str = netsnmp_aal5pvc_fmtaddr(t, NULL, 0); |
110 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", |
111 | 0 | "recv on fd %" NETSNMP_FMT_SKT " got %d bytes (from %s)\n", |
112 | 0 | t->sock, rc, str)); |
113 | 0 | free(str); |
114 | 0 | } |
115 | 0 | } else { |
116 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", |
117 | 0 | "recv on fd %" NETSNMP_FMT_SKT " err %d (\"%s\")\n", |
118 | 0 | t->sock, errno, strerror(errno))); |
119 | 0 | } |
120 | 0 | *opaque = NULL; |
121 | 0 | *olength = 0; |
122 | 0 | } |
123 | 0 | return rc; |
124 | 0 | } |
125 | | |
126 | | |
127 | | |
128 | | static int |
129 | | netsnmp_aal5pvc_send(netsnmp_transport *t, const void *buf, int size, |
130 | | void **opaque, int *olength) |
131 | 0 | { |
132 | 0 | int rc = -1; |
133 | 0 | const struct sockaddr *to = NULL; |
134 | |
|
135 | 0 | if (opaque != NULL && *opaque != NULL && |
136 | 0 | *olength == sizeof(struct sockaddr_atmpvc)) { |
137 | 0 | to = (const struct sockaddr *) (*opaque); |
138 | 0 | } else if (t != NULL && t->data != NULL && |
139 | 0 | t->data_length == sizeof(struct sockaddr_atmpvc)) { |
140 | 0 | to = (const struct sockaddr *) (t->data); |
141 | 0 | } |
142 | |
|
143 | 0 | if (to && t && NETSNMP_IS_VALID_SOCKET(t->sock)) { |
144 | 0 | DEBUGIF("netsnmp_aal5pvc") { |
145 | 0 | char *str = netsnmp_aal5pvc_fmtaddr(NULL, to, |
146 | 0 | sizeof(struct sockaddr_atmpvc)); |
147 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", |
148 | 0 | "send %d bytes to %s on fd %" NETSNMP_FMT_SKT "\n", |
149 | 0 | size, str, t->sock)); |
150 | 0 | free(str); |
151 | 0 | } |
152 | 0 | while (rc < 0) { |
153 | 0 | rc = sendto(t->sock, buf, size, 0, NULL, 0); |
154 | 0 | if (rc < 0 && errno != EINTR) { |
155 | 0 | break; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | } |
159 | 0 | return rc; |
160 | 0 | } |
161 | | |
162 | | |
163 | | |
164 | | static int |
165 | | netsnmp_aal5pvc_close(netsnmp_transport *t) |
166 | 0 | { |
167 | 0 | int rc; |
168 | |
|
169 | 0 | if (!NETSNMP_IS_VALID_SOCKET(t->sock)) |
170 | 0 | return -1; |
171 | | |
172 | 0 | #ifndef HAVE_CLOSESOCKET |
173 | 0 | rc = close(t->sock); |
174 | | #else |
175 | | rc = closesocket(t->sock); |
176 | | #endif |
177 | 0 | t->sock = NETSNMP_INVALID_SOCKET; |
178 | |
|
179 | 0 | return rc; |
180 | 0 | } |
181 | | |
182 | | |
183 | | |
184 | | /* |
185 | | * Open an AAL5 PVC transport for SNMP. Local is TRUE if addr is the local |
186 | | * NSAP to bind to (i.e. this is a server-type session); otherwise addr is |
187 | | * the remote NSAP to send things to. |
188 | | */ |
189 | | |
190 | | netsnmp_transport * |
191 | | netsnmp_aal5pvc_transport(const struct sockaddr_atmpvc *addr, int local) |
192 | 0 | { |
193 | 0 | netsnmp_transport *t = NULL; |
194 | |
|
195 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
196 | | if (local) |
197 | | return NULL; |
198 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
199 | |
|
200 | 0 | if (addr == NULL || addr->sap_family != AF_ATMPVC) { |
201 | 0 | return NULL; |
202 | 0 | } |
203 | | |
204 | 0 | t = netsnmp_transport_alloc(); |
205 | 0 | if (t == NULL) { |
206 | 0 | return NULL; |
207 | 0 | } |
208 | | |
209 | 0 | DEBUGIF("netsnmp_aal5pvc") { |
210 | 0 | char *str = netsnmp_aal5pvc_fmtaddr(NULL, addr, |
211 | 0 | sizeof(struct sockaddr_atmpvc)); |
212 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "open %s %s\n", |
213 | 0 | local ? "local" : "remote", str)); |
214 | 0 | free(str); |
215 | 0 | } |
216 | |
|
217 | 0 | t->domain = netsnmp_AAL5PVCDomain; |
218 | 0 | t->domain_length = |
219 | 0 | sizeof(netsnmp_AAL5PVCDomain) / sizeof(netsnmp_AAL5PVCDomain[0]); |
220 | |
|
221 | 0 | t->sock = socket(PF_ATMPVC, SOCK_DGRAM, 0); |
222 | 0 | if (!NETSNMP_IS_VALID_SOCKET(t->sock)) { |
223 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc","socket failed (%s)\n",strerror(errno))); |
224 | 0 | netsnmp_transport_free(t); |
225 | 0 | return NULL; |
226 | 0 | } |
227 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "fd %" NETSNMP_FMT_SKT " opened\n", |
228 | 0 | t->sock)); |
229 | |
|
230 | 0 | { |
231 | | /* |
232 | | * Set up the QOS parameters. |
233 | | */ |
234 | |
|
235 | 0 | struct atm_qos qos = { }; |
236 | 0 | qos.aal = ATM_AAL5; |
237 | 0 | qos.rxtp.traffic_class = ATM_UBR; |
238 | 0 | qos.rxtp.max_sdu = SNMP_MAX_LEN; /* Hmm -- this is a bit small? */ |
239 | 0 | qos.txtp = qos.rxtp; |
240 | |
|
241 | 0 | if (setsockopt(t->sock, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0) { |
242 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "setsockopt failed (%s)\n", |
243 | 0 | strerror(errno))); |
244 | 0 | netsnmp_aal5pvc_close(t); |
245 | 0 | netsnmp_transport_free(t); |
246 | 0 | return NULL; |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | 0 | if (local) { |
251 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
252 | 0 | t->local_length = sizeof(*addr); |
253 | 0 | t->local = netsnmp_memdup(addr, sizeof(*addr)); |
254 | 0 | if (t->local == NULL) { |
255 | 0 | netsnmp_transport_free(t); |
256 | 0 | return NULL; |
257 | 0 | } |
258 | | |
259 | 0 | if (bind(t->sock, (const struct sockaddr *)addr, |
260 | 0 | sizeof(struct sockaddr_atmpvc)) < 0) { |
261 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "bind failed (%s)\n", |
262 | 0 | strerror(errno))); |
263 | 0 | netsnmp_aal5pvc_close(t); |
264 | 0 | netsnmp_transport_free(t); |
265 | 0 | return NULL; |
266 | 0 | } |
267 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
268 | | return NULL; |
269 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
270 | 0 | } else { |
271 | 0 | t->remote_length = sizeof(*addr); |
272 | 0 | t->remote = netsnmp_memdup(addr, sizeof(*addr)); |
273 | 0 | if (t->remote == NULL) { |
274 | 0 | netsnmp_transport_free(t); |
275 | 0 | return NULL; |
276 | 0 | } |
277 | | |
278 | 0 | if (connect(t->sock, (const struct sockaddr *)addr, |
279 | 0 | sizeof(struct sockaddr_atmpvc)) < 0) { |
280 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "connect failed (%s)\n", |
281 | 0 | strerror(errno))); |
282 | 0 | netsnmp_aal5pvc_close(t); |
283 | 0 | netsnmp_transport_free(t); |
284 | 0 | return NULL; |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | 0 | t->data = malloc(sizeof(struct sockaddr_atmpvc)); |
289 | 0 | if (t->data == NULL) { |
290 | 0 | netsnmp_transport_free(t); |
291 | 0 | return NULL; |
292 | 0 | } |
293 | 0 | memcpy(t->data, addr, sizeof(struct sockaddr_atmpvc)); |
294 | 0 | t->data_length = sizeof(struct sockaddr_atmpvc); |
295 | | |
296 | | /* |
297 | | * 16-bit length field in the trailer, no headers. |
298 | | */ |
299 | |
|
300 | 0 | t->msgMaxSize = 0xffff; |
301 | 0 | t->f_recv = netsnmp_aal5pvc_recv; |
302 | 0 | t->f_send = netsnmp_aal5pvc_send; |
303 | 0 | t->f_close = netsnmp_aal5pvc_close; |
304 | 0 | t->f_accept = NULL; |
305 | 0 | t->f_fmtaddr = netsnmp_aal5pvc_fmtaddr; |
306 | 0 | t->f_get_taddr = netsnmp_aal5pvc_get_taddr; |
307 | |
|
308 | 0 | return t; |
309 | 0 | } |
310 | | |
311 | | |
312 | | |
313 | | netsnmp_transport * |
314 | | netsnmp_aal5pvc_create_tstring(const char *str, int local, |
315 | | const char *default_target) |
316 | 0 | { |
317 | 0 | struct sockaddr_atmpvc addr; |
318 | |
|
319 | 0 | if (str == NULL || *str == '\0') |
320 | 0 | str = default_target; |
321 | |
|
322 | 0 | if (str != NULL) { |
323 | 0 | addr.sap_family = AF_ATMPVC; |
324 | |
|
325 | 0 | if (sscanf(str, "%hd.%hd.%d", &(addr.sap_addr.itf), |
326 | 0 | &(addr.sap_addr.vpi), &(addr.sap_addr.vci)) == 3) { |
327 | 0 | return netsnmp_aal5pvc_transport(&addr, local); |
328 | 0 | } else if (sscanf(str, "%hd.%d", &(addr.sap_addr.vpi), |
329 | 0 | &(addr.sap_addr.vci)) == 2) { |
330 | 0 | addr.sap_addr.itf = 0; |
331 | 0 | return netsnmp_aal5pvc_transport(&addr, local); |
332 | 0 | } else if (sscanf(str, "%d", &(addr.sap_addr.vci)) == 1) { |
333 | 0 | addr.sap_addr.itf = 0; |
334 | 0 | addr.sap_addr.vpi = 0; |
335 | 0 | return netsnmp_aal5pvc_transport(&addr, local); |
336 | 0 | } else { |
337 | 0 | return NULL; |
338 | 0 | } |
339 | 0 | } else { |
340 | 0 | return NULL; |
341 | 0 | } |
342 | 0 | } |
343 | | |
344 | | static int netsnmp_aal5pvc_ostring_to_sockaddr(struct sockaddr_atmpvc *sa, |
345 | | const void *o, size_t o_len) |
346 | 0 | { |
347 | 0 | const unsigned char *p = o; |
348 | |
|
349 | 0 | if (o_len != 8) |
350 | 0 | return 0; |
351 | | |
352 | 0 | memset(sa, 0, sizeof(*sa)); |
353 | 0 | sa->sap_family = AF_ATMPVC; |
354 | 0 | sa->sap_addr.itf = (p[0] << 8) + (p[1] << 0); |
355 | 0 | sa->sap_addr.vpi = (p[2] << 8) + (p[3] << 0); |
356 | 0 | sa->sap_addr.vci = (p[4] << 24) + (p[5] << 16) + (p[6] << 8) + (p[7] << 0); |
357 | 0 | return 1; |
358 | 0 | } |
359 | | |
360 | | netsnmp_transport * |
361 | | netsnmp_aal5pvc_create_ostring(const void *o, size_t o_len, int local) |
362 | 0 | { |
363 | 0 | struct sockaddr_atmpvc sa; |
364 | |
|
365 | 0 | if (netsnmp_aal5pvc_ostring_to_sockaddr(&sa, o, o_len)) |
366 | 0 | return netsnmp_aal5pvc_transport(&sa, local); |
367 | 0 | return NULL; |
368 | 0 | } |
369 | | |
370 | | |
371 | | |
372 | | void |
373 | | netsnmp_aal5pvc_ctor(void) |
374 | 4.20k | { |
375 | 4.20k | aal5pvcDomain.name = netsnmp_AAL5PVCDomain; |
376 | 4.20k | aal5pvcDomain.name_length = OID_LENGTH(netsnmp_AAL5PVCDomain); |
377 | 4.20k | aal5pvcDomain.prefix = calloc(3, sizeof(char *)); |
378 | 4.20k | if (!aal5pvcDomain.prefix) { |
379 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
380 | 0 | return; |
381 | 0 | } |
382 | 4.20k | aal5pvcDomain.prefix[0] = "aal5pvc"; |
383 | 4.20k | aal5pvcDomain.prefix[1] = "pvc"; |
384 | | |
385 | 4.20k | aal5pvcDomain.f_create_from_tstring_new = netsnmp_aal5pvc_create_tstring; |
386 | 4.20k | aal5pvcDomain.f_create_from_ostring = netsnmp_aal5pvc_create_ostring; |
387 | | |
388 | 4.20k | netsnmp_tdomain_register(&aal5pvcDomain); |
389 | 4.20k | } |