/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 != NULL && t->sock >= 0) { |
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 %d got %d bytes (from %s)\n", t->sock, |
112 | 0 | rc, str)); |
113 | 0 | free(str); |
114 | 0 | } |
115 | 0 | } else { |
116 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "recv on fd %d err %d (\"%s\")\n", |
117 | 0 | t->sock, errno, strerror(errno))); |
118 | 0 | } |
119 | 0 | *opaque = NULL; |
120 | 0 | *olength = 0; |
121 | 0 | } |
122 | 0 | return rc; |
123 | 0 | } |
124 | | |
125 | | |
126 | | |
127 | | static int |
128 | | netsnmp_aal5pvc_send(netsnmp_transport *t, const void *buf, int size, |
129 | | void **opaque, int *olength) |
130 | 0 | { |
131 | 0 | int rc = -1; |
132 | 0 | const struct sockaddr *to = NULL; |
133 | |
|
134 | 0 | if (opaque != NULL && *opaque != NULL && |
135 | 0 | *olength == sizeof(struct sockaddr_atmpvc)) { |
136 | 0 | to = (const struct sockaddr *) (*opaque); |
137 | 0 | } else if (t != NULL && t->data != NULL && |
138 | 0 | t->data_length == sizeof(struct sockaddr_atmpvc)) { |
139 | 0 | to = (const struct sockaddr *) (t->data); |
140 | 0 | } |
141 | |
|
142 | 0 | if (to != NULL && t != NULL && t->sock >= 0) { |
143 | 0 | DEBUGIF("netsnmp_aal5pvc") { |
144 | 0 | char *str = netsnmp_aal5pvc_fmtaddr(NULL, to, |
145 | 0 | sizeof(struct sockaddr_atmpvc)); |
146 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", |
147 | 0 | "send %d bytes from %p to %s on fd %d\n", |
148 | 0 | size, buf, str, t->sock)); |
149 | 0 | free(str); |
150 | 0 | } |
151 | 0 | while (rc < 0) { |
152 | 0 | rc = sendto(t->sock, buf, size, 0, NULL, 0); |
153 | 0 | if (rc < 0 && errno != EINTR) { |
154 | 0 | break; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | } |
158 | 0 | return rc; |
159 | 0 | } |
160 | | |
161 | | |
162 | | |
163 | | static int |
164 | | netsnmp_aal5pvc_close(netsnmp_transport *t) |
165 | 0 | { |
166 | 0 | int rc = -1; |
167 | |
|
168 | 0 | if (t->sock >= 0) { |
169 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "close fd %d\n", t->sock)); |
170 | 0 | #ifndef HAVE_CLOSESOCKET |
171 | 0 | rc = close(t->sock); |
172 | | #else |
173 | | rc = closesocket(t->sock); |
174 | | #endif |
175 | 0 | t->sock = -1; |
176 | 0 | } |
177 | 0 | return rc; |
178 | 0 | } |
179 | | |
180 | | |
181 | | |
182 | | /* |
183 | | * Open an AAL5 PVC transport for SNMP. Local is TRUE if addr is the local |
184 | | * NSAP to bind to (i.e. this is a server-type session); otherwise addr is |
185 | | * the remote NSAP to send things to. |
186 | | */ |
187 | | |
188 | | netsnmp_transport * |
189 | | netsnmp_aal5pvc_transport(const struct sockaddr_atmpvc *addr, int local) |
190 | 0 | { |
191 | 0 | netsnmp_transport *t = NULL; |
192 | |
|
193 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
194 | | if (local) |
195 | | return NULL; |
196 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
197 | |
|
198 | 0 | if (addr == NULL || addr->sap_family != AF_ATMPVC) { |
199 | 0 | return NULL; |
200 | 0 | } |
201 | | |
202 | 0 | t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
203 | 0 | if (t == NULL) { |
204 | 0 | return NULL; |
205 | 0 | } |
206 | | |
207 | 0 | DEBUGIF("netsnmp_aal5pvc") { |
208 | 0 | char *str = netsnmp_aal5pvc_fmtaddr(NULL, addr, |
209 | 0 | sizeof(struct sockaddr_atmpvc)); |
210 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "open %s %s\n", |
211 | 0 | local ? "local" : "remote", str)); |
212 | 0 | free(str); |
213 | 0 | } |
214 | |
|
215 | 0 | t->domain = netsnmp_AAL5PVCDomain; |
216 | 0 | t->domain_length = |
217 | 0 | sizeof(netsnmp_AAL5PVCDomain) / sizeof(netsnmp_AAL5PVCDomain[0]); |
218 | |
|
219 | 0 | t->sock = socket(PF_ATMPVC, SOCK_DGRAM, 0); |
220 | 0 | if (t->sock < 0) { |
221 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc","socket failed (%s)\n",strerror(errno))); |
222 | 0 | netsnmp_transport_free(t); |
223 | 0 | return NULL; |
224 | 0 | } |
225 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "fd %d opened\n", t->sock)); |
226 | |
|
227 | 0 | { |
228 | | /* |
229 | | * Set up the QOS parameters. |
230 | | */ |
231 | |
|
232 | 0 | struct atm_qos qos = { }; |
233 | 0 | qos.aal = ATM_AAL5; |
234 | 0 | qos.rxtp.traffic_class = ATM_UBR; |
235 | 0 | qos.rxtp.max_sdu = SNMP_MAX_LEN; /* Hmm -- this is a bit small? */ |
236 | 0 | qos.txtp = qos.rxtp; |
237 | |
|
238 | 0 | if (setsockopt(t->sock, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0) { |
239 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "setsockopt failed (%s)\n", |
240 | 0 | strerror(errno))); |
241 | 0 | netsnmp_aal5pvc_close(t); |
242 | 0 | netsnmp_transport_free(t); |
243 | 0 | return NULL; |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | 0 | if (local) { |
248 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
249 | 0 | t->local_length = sizeof(*addr); |
250 | 0 | t->local = netsnmp_memdup(addr, sizeof(*addr)); |
251 | 0 | if (t->local == NULL) { |
252 | 0 | netsnmp_transport_free(t); |
253 | 0 | return NULL; |
254 | 0 | } |
255 | | |
256 | 0 | if (bind(t->sock, (const struct sockaddr *)addr, |
257 | 0 | sizeof(struct sockaddr_atmpvc)) < 0) { |
258 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "bind failed (%s)\n", |
259 | 0 | strerror(errno))); |
260 | 0 | netsnmp_aal5pvc_close(t); |
261 | 0 | netsnmp_transport_free(t); |
262 | 0 | return NULL; |
263 | 0 | } |
264 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
265 | | return NULL; |
266 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
267 | 0 | } else { |
268 | 0 | t->remote_length = sizeof(*addr); |
269 | 0 | t->remote = netsnmp_memdup(addr, sizeof(*addr)); |
270 | 0 | if (t->remote == NULL) { |
271 | 0 | netsnmp_transport_free(t); |
272 | 0 | return NULL; |
273 | 0 | } |
274 | | |
275 | 0 | if (connect(t->sock, (const struct sockaddr *)addr, |
276 | 0 | sizeof(struct sockaddr_atmpvc)) < 0) { |
277 | 0 | DEBUGMSGTL(("netsnmp_aal5pvc", "connect failed (%s)\n", |
278 | 0 | strerror(errno))); |
279 | 0 | netsnmp_aal5pvc_close(t); |
280 | 0 | netsnmp_transport_free(t); |
281 | 0 | return NULL; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | 0 | t->data = malloc(sizeof(struct sockaddr_atmpvc)); |
286 | 0 | if (t->data == NULL) { |
287 | 0 | netsnmp_transport_free(t); |
288 | 0 | return NULL; |
289 | 0 | } |
290 | 0 | memcpy(t->data, addr, sizeof(struct sockaddr_atmpvc)); |
291 | 0 | t->data_length = sizeof(struct sockaddr_atmpvc); |
292 | | |
293 | | /* |
294 | | * 16-bit length field in the trailer, no headers. |
295 | | */ |
296 | |
|
297 | 0 | t->msgMaxSize = 0xffff; |
298 | 0 | t->f_recv = netsnmp_aal5pvc_recv; |
299 | 0 | t->f_send = netsnmp_aal5pvc_send; |
300 | 0 | t->f_close = netsnmp_aal5pvc_close; |
301 | 0 | t->f_accept = NULL; |
302 | 0 | t->f_fmtaddr = netsnmp_aal5pvc_fmtaddr; |
303 | 0 | t->f_get_taddr = netsnmp_aal5pvc_get_taddr; |
304 | |
|
305 | 0 | return t; |
306 | 0 | } |
307 | | |
308 | | |
309 | | |
310 | | netsnmp_transport * |
311 | | netsnmp_aal5pvc_create_tstring(const char *str, int local, |
312 | | const char *default_target) |
313 | 0 | { |
314 | 0 | struct sockaddr_atmpvc addr; |
315 | |
|
316 | 0 | if (str == NULL || *str == '\0') |
317 | 0 | str = default_target; |
318 | |
|
319 | 0 | if (str != NULL) { |
320 | 0 | addr.sap_family = AF_ATMPVC; |
321 | |
|
322 | 0 | if (sscanf(str, "%hd.%hd.%d", &(addr.sap_addr.itf), |
323 | 0 | &(addr.sap_addr.vpi), &(addr.sap_addr.vci)) == 3) { |
324 | 0 | return netsnmp_aal5pvc_transport(&addr, local); |
325 | 0 | } else if (sscanf(str, "%hd.%d", &(addr.sap_addr.vpi), |
326 | 0 | &(addr.sap_addr.vci)) == 2) { |
327 | 0 | addr.sap_addr.itf = 0; |
328 | 0 | return netsnmp_aal5pvc_transport(&addr, local); |
329 | 0 | } else if (sscanf(str, "%d", &(addr.sap_addr.vci)) == 1) { |
330 | 0 | addr.sap_addr.itf = 0; |
331 | 0 | addr.sap_addr.vpi = 0; |
332 | 0 | return netsnmp_aal5pvc_transport(&addr, local); |
333 | 0 | } else { |
334 | 0 | return NULL; |
335 | 0 | } |
336 | 0 | } else { |
337 | 0 | return NULL; |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | static int netsnmp_aal5pvc_ostring_to_sockaddr(struct sockaddr_atmpvc *sa, |
342 | | const void *o, size_t o_len) |
343 | 0 | { |
344 | 0 | const unsigned char *p = o; |
345 | |
|
346 | 0 | if (o_len != 8) |
347 | 0 | return 0; |
348 | | |
349 | 0 | memset(sa, 0, sizeof(*sa)); |
350 | 0 | sa->sap_family = AF_ATMPVC; |
351 | 0 | sa->sap_addr.itf = (p[0] << 8) + (p[1] << 0); |
352 | 0 | sa->sap_addr.vpi = (p[2] << 8) + (p[3] << 0); |
353 | 0 | sa->sap_addr.vci = (p[4] << 24) + (p[5] << 16) + (p[6] << 8) + (p[7] << 0); |
354 | 0 | return 1; |
355 | 0 | } |
356 | | |
357 | | netsnmp_transport * |
358 | | netsnmp_aal5pvc_create_ostring(const void *o, size_t o_len, int local) |
359 | 0 | { |
360 | 0 | struct sockaddr_atmpvc sa; |
361 | |
|
362 | 0 | if (netsnmp_aal5pvc_ostring_to_sockaddr(&sa, o, o_len)) |
363 | 0 | return netsnmp_aal5pvc_transport(&sa, local); |
364 | 0 | return NULL; |
365 | 0 | } |
366 | | |
367 | | |
368 | | |
369 | | void |
370 | | netsnmp_aal5pvc_ctor(void) |
371 | 4.25k | { |
372 | 4.25k | aal5pvcDomain.name = netsnmp_AAL5PVCDomain; |
373 | 4.25k | aal5pvcDomain.name_length = OID_LENGTH(netsnmp_AAL5PVCDomain); |
374 | 4.25k | aal5pvcDomain.prefix = calloc(3, sizeof(char *)); |
375 | 4.25k | if (!aal5pvcDomain.prefix) { |
376 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
377 | 0 | return; |
378 | 0 | } |
379 | 4.25k | aal5pvcDomain.prefix[0] = "aal5pvc"; |
380 | 4.25k | aal5pvcDomain.prefix[1] = "pvc"; |
381 | | |
382 | 4.25k | aal5pvcDomain.f_create_from_tstring_new = netsnmp_aal5pvc_create_tstring; |
383 | 4.25k | aal5pvcDomain.f_create_from_ostring = netsnmp_aal5pvc_create_ostring; |
384 | | |
385 | 4.25k | netsnmp_tdomain_register(&aal5pvcDomain); |
386 | 4.25k | } |