Deleted Added
full compact
ng_cisco.c (67506) ng_cisco.c (68876)
1
2/*
3 * ng_cisco.c
4 *
5 * Copyright (c) 1996-1999 Whistle Communications, Inc.
6 * All rights reserved.
7 *
8 * Subject to the following obligations and disclaimer of warranty, use and
9 * redistribution of this software, in source or object code forms, with or
10 * without modifications are expressly permitted by Whistle Communications;
11 * provided, however, that:
12 * 1. Any and all reproductions of the source or object code must include the
13 * copyright notice above and the following disclaimer of warranties; and
14 * 2. No rights are granted, in any manner or form, to use Whistle
15 * Communications, Inc. trademarks, including the mark "WHISTLE
16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17 * such appears in the above copyright notice or in the software.
18 *
19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 *
37 * Author: Julian Elischer <julian@freebsd.org>
38 *
1
2/*
3 * ng_cisco.c
4 *
5 * Copyright (c) 1996-1999 Whistle Communications, Inc.
6 * All rights reserved.
7 *
8 * Subject to the following obligations and disclaimer of warranty, use and
9 * redistribution of this software, in source or object code forms, with or
10 * without modifications are expressly permitted by Whistle Communications;
11 * provided, however, that:
12 * 1. Any and all reproductions of the source or object code must include the
13 * copyright notice above and the following disclaimer of warranties; and
14 * 2. No rights are granted, in any manner or form, to use Whistle
15 * Communications, Inc. trademarks, including the mark "WHISTLE
16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17 * such appears in the above copyright notice or in the software.
18 *
19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 *
37 * Author: Julian Elischer <julian@freebsd.org>
38 *
39 * $FreeBSD: head/sys/netgraph/ng_cisco.c 67506 2000-10-24 17:32:45Z julian $
39 * $FreeBSD: head/sys/netgraph/ng_cisco.c 68876 2000-11-18 15:17:43Z dwmalone $
40 * $Whistle: ng_cisco.c,v 1.25 1999/11/01 09:24:51 julian Exp $
41 */
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/errno.h>
46#include <sys/kernel.h>
47#include <sys/socket.h>
48#include <sys/malloc.h>
49#include <sys/mbuf.h>
50#include <sys/syslog.h>
51
52#include <net/if.h>
53
54#include <netinet/in.h>
55#include <netinet/if_ether.h>
56
57#include <netatalk/at.h>
58
59#include <netipx/ipx.h>
60#include <netipx/ipx_if.h>
61
62#include <netgraph/ng_message.h>
63#include <netgraph/netgraph.h>
64#include <netgraph/ng_parse.h>
65#include <netgraph/ng_cisco.h>
66
67#define CISCO_MULTICAST 0x8f /* Cisco multicast address */
68#define CISCO_UNICAST 0x0f /* Cisco unicast address */
69#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
70#define CISCO_ADDR_REQ 0 /* Cisco address request */
71#define CISCO_ADDR_REPLY 1 /* Cisco address reply */
72#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
73
74#define KEEPALIVE_SECS 10
75
76struct cisco_header {
77 u_char address;
78 u_char control;
79 u_short protocol;
80};
81
82#define CISCO_HEADER_LEN sizeof (struct cisco_header)
83
84struct cisco_packet {
85 u_long type;
86 u_long par1;
87 u_long par2;
88 u_short rel;
89 u_short time0;
90 u_short time1;
91};
92
93#define CISCO_PACKET_LEN (sizeof(struct cisco_packet))
94
95struct protoent {
96 hook_p hook; /* the hook for this proto */
97 u_short af; /* address family, -1 = downstream */
98};
99
100struct cisco_priv {
101 u_long local_seq;
102 u_long remote_seq;
103 u_long seqRetries; /* how many times we've been here throwing out
104 * the same sequence number without ack */
105 node_p node;
106 struct callout_handle handle;
107 struct protoent downstream;
108 struct protoent inet; /* IP information */
109 struct in_addr localip;
110 struct in_addr localmask;
111 struct protoent inet6; /* IPv6 information */
112 struct protoent atalk; /* AppleTalk information */
113 struct protoent ipx; /* IPX information */
114};
115typedef struct cisco_priv *sc_p;
116
117/* Netgraph methods */
118static ng_constructor_t cisco_constructor;
119static ng_rcvmsg_t cisco_rcvmsg;
120static ng_shutdown_t cisco_rmnode;
121static ng_newhook_t cisco_newhook;
122static ng_rcvdata_t cisco_rcvdata;
123static ng_disconnect_t cisco_disconnect;
124
125/* Other functions */
126static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta);
127static void cisco_keepalive(void *arg);
128static int cisco_send(sc_p sc, int type, long par1, long par2);
129
130/* Parse type for struct ng_cisco_ipaddr */
131static const struct ng_parse_struct_info
132 ng_cisco_ipaddr_type_info = NG_CISCO_IPADDR_TYPE_INFO;
133static const struct ng_parse_type ng_cisco_ipaddr_type = {
134 &ng_parse_struct_type,
135 &ng_cisco_ipaddr_type_info
136};
137
138/* Parse type for struct ng_async_stat */
139static const struct ng_parse_struct_info
140 ng_cisco_stats_type_info = NG_CISCO_STATS_TYPE_INFO;
141static const struct ng_parse_type ng_cisco_stats_type = {
142 &ng_parse_struct_type,
143 &ng_cisco_stats_type_info,
144};
145
146/* List of commands and how to convert arguments to/from ASCII */
147static const struct ng_cmdlist ng_cisco_cmdlist[] = {
148 {
149 NGM_CISCO_COOKIE,
150 NGM_CISCO_SET_IPADDR,
151 "setipaddr",
152 &ng_cisco_ipaddr_type,
153 NULL
154 },
155 {
156 NGM_CISCO_COOKIE,
157 NGM_CISCO_GET_IPADDR,
158 "getipaddr",
159 NULL,
160 &ng_cisco_ipaddr_type
161 },
162 {
163 NGM_CISCO_COOKIE,
164 NGM_CISCO_GET_STATUS,
165 "getstats",
166 NULL,
167 &ng_cisco_stats_type
168 },
169 { 0 }
170};
171
172/* Node type */
173static struct ng_type typestruct = {
174 NG_VERSION,
175 NG_CISCO_NODE_TYPE,
176 NULL,
177 cisco_constructor,
178 cisco_rcvmsg,
179 cisco_rmnode,
180 cisco_newhook,
181 NULL,
182 NULL,
183 cisco_rcvdata,
184 cisco_rcvdata,
185 cisco_disconnect,
186 ng_cisco_cmdlist
187};
188NETGRAPH_INIT(cisco, &typestruct);
189
190/*
191 * Node constructor
192 */
193static int
194cisco_constructor(node_p *nodep)
195{
196 sc_p sc;
197 int error = 0;
198
40 * $Whistle: ng_cisco.c,v 1.25 1999/11/01 09:24:51 julian Exp $
41 */
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/errno.h>
46#include <sys/kernel.h>
47#include <sys/socket.h>
48#include <sys/malloc.h>
49#include <sys/mbuf.h>
50#include <sys/syslog.h>
51
52#include <net/if.h>
53
54#include <netinet/in.h>
55#include <netinet/if_ether.h>
56
57#include <netatalk/at.h>
58
59#include <netipx/ipx.h>
60#include <netipx/ipx_if.h>
61
62#include <netgraph/ng_message.h>
63#include <netgraph/netgraph.h>
64#include <netgraph/ng_parse.h>
65#include <netgraph/ng_cisco.h>
66
67#define CISCO_MULTICAST 0x8f /* Cisco multicast address */
68#define CISCO_UNICAST 0x0f /* Cisco unicast address */
69#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
70#define CISCO_ADDR_REQ 0 /* Cisco address request */
71#define CISCO_ADDR_REPLY 1 /* Cisco address reply */
72#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
73
74#define KEEPALIVE_SECS 10
75
76struct cisco_header {
77 u_char address;
78 u_char control;
79 u_short protocol;
80};
81
82#define CISCO_HEADER_LEN sizeof (struct cisco_header)
83
84struct cisco_packet {
85 u_long type;
86 u_long par1;
87 u_long par2;
88 u_short rel;
89 u_short time0;
90 u_short time1;
91};
92
93#define CISCO_PACKET_LEN (sizeof(struct cisco_packet))
94
95struct protoent {
96 hook_p hook; /* the hook for this proto */
97 u_short af; /* address family, -1 = downstream */
98};
99
100struct cisco_priv {
101 u_long local_seq;
102 u_long remote_seq;
103 u_long seqRetries; /* how many times we've been here throwing out
104 * the same sequence number without ack */
105 node_p node;
106 struct callout_handle handle;
107 struct protoent downstream;
108 struct protoent inet; /* IP information */
109 struct in_addr localip;
110 struct in_addr localmask;
111 struct protoent inet6; /* IPv6 information */
112 struct protoent atalk; /* AppleTalk information */
113 struct protoent ipx; /* IPX information */
114};
115typedef struct cisco_priv *sc_p;
116
117/* Netgraph methods */
118static ng_constructor_t cisco_constructor;
119static ng_rcvmsg_t cisco_rcvmsg;
120static ng_shutdown_t cisco_rmnode;
121static ng_newhook_t cisco_newhook;
122static ng_rcvdata_t cisco_rcvdata;
123static ng_disconnect_t cisco_disconnect;
124
125/* Other functions */
126static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta);
127static void cisco_keepalive(void *arg);
128static int cisco_send(sc_p sc, int type, long par1, long par2);
129
130/* Parse type for struct ng_cisco_ipaddr */
131static const struct ng_parse_struct_info
132 ng_cisco_ipaddr_type_info = NG_CISCO_IPADDR_TYPE_INFO;
133static const struct ng_parse_type ng_cisco_ipaddr_type = {
134 &ng_parse_struct_type,
135 &ng_cisco_ipaddr_type_info
136};
137
138/* Parse type for struct ng_async_stat */
139static const struct ng_parse_struct_info
140 ng_cisco_stats_type_info = NG_CISCO_STATS_TYPE_INFO;
141static const struct ng_parse_type ng_cisco_stats_type = {
142 &ng_parse_struct_type,
143 &ng_cisco_stats_type_info,
144};
145
146/* List of commands and how to convert arguments to/from ASCII */
147static const struct ng_cmdlist ng_cisco_cmdlist[] = {
148 {
149 NGM_CISCO_COOKIE,
150 NGM_CISCO_SET_IPADDR,
151 "setipaddr",
152 &ng_cisco_ipaddr_type,
153 NULL
154 },
155 {
156 NGM_CISCO_COOKIE,
157 NGM_CISCO_GET_IPADDR,
158 "getipaddr",
159 NULL,
160 &ng_cisco_ipaddr_type
161 },
162 {
163 NGM_CISCO_COOKIE,
164 NGM_CISCO_GET_STATUS,
165 "getstats",
166 NULL,
167 &ng_cisco_stats_type
168 },
169 { 0 }
170};
171
172/* Node type */
173static struct ng_type typestruct = {
174 NG_VERSION,
175 NG_CISCO_NODE_TYPE,
176 NULL,
177 cisco_constructor,
178 cisco_rcvmsg,
179 cisco_rmnode,
180 cisco_newhook,
181 NULL,
182 NULL,
183 cisco_rcvdata,
184 cisco_rcvdata,
185 cisco_disconnect,
186 ng_cisco_cmdlist
187};
188NETGRAPH_INIT(cisco, &typestruct);
189
190/*
191 * Node constructor
192 */
193static int
194cisco_constructor(node_p *nodep)
195{
196 sc_p sc;
197 int error = 0;
198
199 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT);
199 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
200 if (sc == NULL)
201 return (ENOMEM);
200 if (sc == NULL)
201 return (ENOMEM);
202 bzero(sc, sizeof(struct cisco_priv));
203
204 callout_handle_init(&sc->handle);
205 if ((error = ng_make_node_common(&typestruct, nodep))) {
206 FREE(sc, M_NETGRAPH);
207 return (error);
208 }
209 (*nodep)->private = sc;
210 sc->node = *nodep;
211
212 /* Initialise the varous protocol hook holders */
213 sc->downstream.af = 0xffff;
214 sc->inet.af = AF_INET;
215 sc->inet6.af = AF_INET6;
216 sc->atalk.af = AF_APPLETALK;
217 sc->ipx.af = AF_IPX;
218 return (0);
219}
220
221/*
222 * Check new hook
223 */
224static int
225cisco_newhook(node_p node, hook_p hook, const char *name)
226{
227 const sc_p sc = node->private;
228
229 if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) {
230 sc->downstream.hook = hook;
231 hook->private = &sc->downstream;
232
233 /* Start keepalives */
234 sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS);
235 } else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) {
236 sc->inet.hook = hook;
237 hook->private = &sc->inet;
238 } else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) {
239 sc->atalk.hook = hook;
240 hook->private = &sc->atalk;
241 } else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) {
242 sc->ipx.hook = hook;
243 hook->private = &sc->ipx;
244 } else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) {
245 hook->private = NULL; /* unimplemented */
246 } else
247 return (EINVAL);
248 return 0;
249}
250
251/*
252 * Receive control message.
253 */
254static int
255cisco_rcvmsg(node_p node, struct ng_mesg *msg,
256 const char *retaddr, struct ng_mesg **rptr, hook_p lasthook)
257{
258 const sc_p sc = node->private;
259 struct ng_mesg *resp = NULL;
260 int error = 0;
261
262 switch (msg->header.typecookie) {
263 case NGM_GENERIC_COOKIE:
264 switch (msg->header.cmd) {
265 case NGM_TEXT_STATUS:
266 {
267 char *arg;
268 int pos;
269
270 NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg)
271 + NG_TEXTRESPONSE, M_NOWAIT);
272 if (resp == NULL) {
273 error = ENOMEM;
274 break;
275 }
276 arg = (char *) resp->data;
277 pos = sprintf(arg,
278 "keepalive period: %d sec; ", KEEPALIVE_SECS);
279 pos += sprintf(arg + pos,
280 "unacknowledged keepalives: %ld", sc->seqRetries);
281 resp->header.arglen = pos + 1;
282 break;
283 }
284 default:
285 error = EINVAL;
286 break;
287 }
288 break;
289 case NGM_CISCO_COOKIE:
290 switch (msg->header.cmd) {
291 case NGM_CISCO_GET_IPADDR: /* could be a late reply! */
292 if ((msg->header.flags & NGF_RESP) == 0) {
293 struct in_addr *ips;
294
295 NG_MKRESPONSE(resp, msg,
296 2 * sizeof(*ips), M_NOWAIT);
297 if (!resp) {
298 error = ENOMEM;
299 break;
300 }
301 ips = (struct in_addr *) resp->data;
302 ips[0] = sc->localip;
303 ips[1] = sc->localmask;
304 break;
305 }
306 /* FALLTHROUGH */ /* ...if it's a reply */
307 case NGM_CISCO_SET_IPADDR:
308 {
309 struct in_addr *const ips = (struct in_addr *)msg->data;
310
311 if (msg->header.arglen < 2 * sizeof(*ips)) {
312 error = EINVAL;
313 break;
314 }
315 sc->localip = ips[0];
316 sc->localmask = ips[1];
317 break;
318 }
319 case NGM_CISCO_GET_STATUS:
320 {
321 struct ng_cisco_stats *stat;
322
323 NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
324 if (!resp) {
325 error = ENOMEM;
326 break;
327 }
328 stat = (struct ng_cisco_stats *)resp->data;
329 stat->seqRetries = sc->seqRetries;
330 stat->keepAlivePeriod = KEEPALIVE_SECS;
331 break;
332 }
333 default:
334 error = EINVAL;
335 break;
336 }
337 break;
338 default:
339 error = EINVAL;
340 break;
341 }
342 if (rptr)
343 *rptr = resp;
344 else if (resp)
345 FREE(resp, M_NETGRAPH);
346 FREE(msg, M_NETGRAPH);
347 return (error);
348}
349
350/*
351 * Receive data
352 */
353static int
354cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
355 struct mbuf **ret_m, meta_p *ret_meta)
356{
357 const sc_p sc = hook->node->private;
358 struct protoent *pep;
359 struct cisco_header *h;
360 int error = 0;
361
362 if ((pep = hook->private) == NULL)
363 goto out;
364
365 /* If it came from our downlink, deal with it separately */
366 if (pep->af == 0xffff)
367 return (cisco_input(sc, m, meta));
368
369 /* OK so it came from a protocol, heading out. Prepend general data
370 packet header. For now, IP,IPX only */
371 M_PREPEND(m, CISCO_HEADER_LEN, M_DONTWAIT);
372 if (!m) {
373 error = ENOBUFS;
374 goto out;
375 }
376 h = mtod(m, struct cisco_header *);
377 h->address = CISCO_UNICAST;
378 h->control = 0;
379
380 switch (pep->af) {
381 case AF_INET: /* Internet Protocol */
382 h->protocol = htons(ETHERTYPE_IP);
383 break;
384 case AF_INET6:
385 h->protocol = htons(ETHERTYPE_IPV6);
386 break;
387 case AF_APPLETALK: /* AppleTalk Protocol */
388 h->protocol = htons(ETHERTYPE_AT);
389 break;
390 case AF_IPX: /* Novell IPX Protocol */
391 h->protocol = htons(ETHERTYPE_IPX);
392 break;
393 default:
394 error = EAFNOSUPPORT;
395 goto out;
396 }
397
398 /* Send it */
399 NG_SEND_DATA(error, sc->downstream.hook, m, meta);
400 return (error);
401
402out:
403 NG_FREE_DATA(m, meta);
404 return (error);
405}
406
407/*
408 * Shutdown node
409 */
410static int
411cisco_rmnode(node_p node)
412{
413 const sc_p sc = node->private;
414
415 node->flags |= NG_INVALID;
416 ng_cutlinks(node);
417 ng_unname(node);
418 node->private = NULL;
419 ng_unref(sc->node);
420 FREE(sc, M_NETGRAPH);
421 return (0);
422}
423
424/*
425 * Disconnection of a hook
426 *
427 * For this type, removal of the last link destroys the node
428 */
429static int
430cisco_disconnect(hook_p hook)
431{
432 const sc_p sc = hook->node->private;
433 struct protoent *pep;
434
435 /* Check it's not the debug hook */
436 if ((pep = hook->private)) {
437 pep->hook = NULL;
438 if (pep->af == 0xffff) {
439 /* If it is the downstream hook, stop the timers */
440 untimeout(cisco_keepalive, sc, sc->handle);
441 }
442 }
443
444 /* If no more hooks, remove the node */
445 if (hook->node->numhooks == 0)
446 ng_rmnode(hook->node);
447 return (0);
448}
449
450/*
451 * Receive data
452 */
453static int
454cisco_input(sc_p sc, struct mbuf *m, meta_p meta)
455{
456 struct cisco_header *h;
457 struct cisco_packet *p;
458 struct protoent *pep;
459 int error = 0;
460
461 if (m->m_pkthdr.len <= CISCO_HEADER_LEN)
462 goto drop;
463
464 /* Strip off cisco header */
465 h = mtod(m, struct cisco_header *);
466 m_adj(m, CISCO_HEADER_LEN);
467
468 switch (h->address) {
469 default: /* Invalid Cisco packet. */
470 goto drop;
471 case CISCO_UNICAST:
472 case CISCO_MULTICAST:
473 /* Don't check the control field here (RFC 1547). */
474 switch (ntohs(h->protocol)) {
475 default:
476 goto drop;
477 case CISCO_KEEPALIVE:
478 p = mtod(m, struct cisco_packet *);
479 switch (ntohl(p->type)) {
480 default:
481 log(LOG_WARNING,
482 "cisco: unknown cisco packet type: 0x%lx\n",
483 ntohl(p->type));
484 break;
485 case CISCO_ADDR_REPLY:
486 /* Reply on address request, ignore */
487 break;
488 case CISCO_KEEPALIVE_REQ:
489 sc->remote_seq = ntohl(p->par1);
490 if (sc->local_seq == ntohl(p->par2)) {
491 sc->local_seq++;
492 sc->seqRetries = 0;
493 }
494 break;
495 case CISCO_ADDR_REQ:
496 {
497 struct ng_mesg *msg, *resp;
498
499 /* Ask inet peer for IP address information */
500 if (sc->inet.hook == NULL)
501 goto nomsg;
502 NG_MKMESSAGE(msg, NGM_CISCO_COOKIE,
503 NGM_CISCO_GET_IPADDR, 0, M_NOWAIT);
504 if (msg == NULL)
505 goto nomsg;
506 ng_send_msg(sc->node, msg,
507 NG_CISCO_HOOK_INET, &resp);
508 if (resp != NULL)
509 cisco_rcvmsg(sc->node, resp, ".",
510 NULL, NULL);
511
512 nomsg:
513 /* Send reply to peer device */
514 error = cisco_send(sc, CISCO_ADDR_REPLY,
515 ntohl(sc->localip.s_addr),
516 ntohl(sc->localmask.s_addr));
517 break;
518 }
519 }
520 goto drop;
521 case ETHERTYPE_IP:
522 pep = &sc->inet;
523 break;
524 case ETHERTYPE_IPV6:
525 pep = &sc->inet6;
526 break;
527 case ETHERTYPE_AT:
528 pep = &sc->atalk;
529 break;
530 case ETHERTYPE_IPX:
531 pep = &sc->ipx;
532 break;
533 }
534 break;
535 }
536
537 /* Send it on */
538 if (pep->hook == NULL)
539 goto drop;
540 NG_SEND_DATA(error, pep->hook, m, meta);
541 return (error);
542
543drop:
544 NG_FREE_DATA(m, meta);
545 return (error);
546}
547
548
549/*
550 * Send keepalive packets, every 10 seconds.
551 */
552static void
553cisco_keepalive(void *arg)
554{
555 const sc_p sc = arg;
556 int s = splimp();
557
558 cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq);
559 sc->seqRetries++;
560 splx(s);
561 sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS);
562}
563
564/*
565 * Send Cisco keepalive packet.
566 */
567static int
568cisco_send(sc_p sc, int type, long par1, long par2)
569{
570 struct cisco_header *h;
571 struct cisco_packet *ch;
572 struct mbuf *m;
573 u_long t;
574 int error = 0;
575 meta_p meta = NULL;
576 struct timeval time;
577
578 getmicrotime(&time);
579
580 MGETHDR(m, M_DONTWAIT, MT_DATA);
581 if (!m)
582 return (ENOBUFS);
583
584 t = (time.tv_sec - boottime.tv_sec) * 1000;
585 m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN;
586 m->m_pkthdr.rcvif = 0;
587
588 h = mtod(m, struct cisco_header *);
589 h->address = CISCO_MULTICAST;
590 h->control = 0;
591 h->protocol = htons(CISCO_KEEPALIVE);
592
593 ch = (struct cisco_packet *) (h + 1);
594 ch->type = htonl(type);
595 ch->par1 = htonl(par1);
596 ch->par2 = htonl(par2);
597 ch->rel = -1;
598 ch->time0 = htons((u_short) (t >> 16));
599 ch->time1 = htons((u_short) t);
600
601 NG_SEND_DATA(error, sc->downstream.hook, m, meta);
602 return (error);
603}
202
203 callout_handle_init(&sc->handle);
204 if ((error = ng_make_node_common(&typestruct, nodep))) {
205 FREE(sc, M_NETGRAPH);
206 return (error);
207 }
208 (*nodep)->private = sc;
209 sc->node = *nodep;
210
211 /* Initialise the varous protocol hook holders */
212 sc->downstream.af = 0xffff;
213 sc->inet.af = AF_INET;
214 sc->inet6.af = AF_INET6;
215 sc->atalk.af = AF_APPLETALK;
216 sc->ipx.af = AF_IPX;
217 return (0);
218}
219
220/*
221 * Check new hook
222 */
223static int
224cisco_newhook(node_p node, hook_p hook, const char *name)
225{
226 const sc_p sc = node->private;
227
228 if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) {
229 sc->downstream.hook = hook;
230 hook->private = &sc->downstream;
231
232 /* Start keepalives */
233 sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS);
234 } else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) {
235 sc->inet.hook = hook;
236 hook->private = &sc->inet;
237 } else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) {
238 sc->atalk.hook = hook;
239 hook->private = &sc->atalk;
240 } else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) {
241 sc->ipx.hook = hook;
242 hook->private = &sc->ipx;
243 } else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) {
244 hook->private = NULL; /* unimplemented */
245 } else
246 return (EINVAL);
247 return 0;
248}
249
250/*
251 * Receive control message.
252 */
253static int
254cisco_rcvmsg(node_p node, struct ng_mesg *msg,
255 const char *retaddr, struct ng_mesg **rptr, hook_p lasthook)
256{
257 const sc_p sc = node->private;
258 struct ng_mesg *resp = NULL;
259 int error = 0;
260
261 switch (msg->header.typecookie) {
262 case NGM_GENERIC_COOKIE:
263 switch (msg->header.cmd) {
264 case NGM_TEXT_STATUS:
265 {
266 char *arg;
267 int pos;
268
269 NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg)
270 + NG_TEXTRESPONSE, M_NOWAIT);
271 if (resp == NULL) {
272 error = ENOMEM;
273 break;
274 }
275 arg = (char *) resp->data;
276 pos = sprintf(arg,
277 "keepalive period: %d sec; ", KEEPALIVE_SECS);
278 pos += sprintf(arg + pos,
279 "unacknowledged keepalives: %ld", sc->seqRetries);
280 resp->header.arglen = pos + 1;
281 break;
282 }
283 default:
284 error = EINVAL;
285 break;
286 }
287 break;
288 case NGM_CISCO_COOKIE:
289 switch (msg->header.cmd) {
290 case NGM_CISCO_GET_IPADDR: /* could be a late reply! */
291 if ((msg->header.flags & NGF_RESP) == 0) {
292 struct in_addr *ips;
293
294 NG_MKRESPONSE(resp, msg,
295 2 * sizeof(*ips), M_NOWAIT);
296 if (!resp) {
297 error = ENOMEM;
298 break;
299 }
300 ips = (struct in_addr *) resp->data;
301 ips[0] = sc->localip;
302 ips[1] = sc->localmask;
303 break;
304 }
305 /* FALLTHROUGH */ /* ...if it's a reply */
306 case NGM_CISCO_SET_IPADDR:
307 {
308 struct in_addr *const ips = (struct in_addr *)msg->data;
309
310 if (msg->header.arglen < 2 * sizeof(*ips)) {
311 error = EINVAL;
312 break;
313 }
314 sc->localip = ips[0];
315 sc->localmask = ips[1];
316 break;
317 }
318 case NGM_CISCO_GET_STATUS:
319 {
320 struct ng_cisco_stats *stat;
321
322 NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
323 if (!resp) {
324 error = ENOMEM;
325 break;
326 }
327 stat = (struct ng_cisco_stats *)resp->data;
328 stat->seqRetries = sc->seqRetries;
329 stat->keepAlivePeriod = KEEPALIVE_SECS;
330 break;
331 }
332 default:
333 error = EINVAL;
334 break;
335 }
336 break;
337 default:
338 error = EINVAL;
339 break;
340 }
341 if (rptr)
342 *rptr = resp;
343 else if (resp)
344 FREE(resp, M_NETGRAPH);
345 FREE(msg, M_NETGRAPH);
346 return (error);
347}
348
349/*
350 * Receive data
351 */
352static int
353cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
354 struct mbuf **ret_m, meta_p *ret_meta)
355{
356 const sc_p sc = hook->node->private;
357 struct protoent *pep;
358 struct cisco_header *h;
359 int error = 0;
360
361 if ((pep = hook->private) == NULL)
362 goto out;
363
364 /* If it came from our downlink, deal with it separately */
365 if (pep->af == 0xffff)
366 return (cisco_input(sc, m, meta));
367
368 /* OK so it came from a protocol, heading out. Prepend general data
369 packet header. For now, IP,IPX only */
370 M_PREPEND(m, CISCO_HEADER_LEN, M_DONTWAIT);
371 if (!m) {
372 error = ENOBUFS;
373 goto out;
374 }
375 h = mtod(m, struct cisco_header *);
376 h->address = CISCO_UNICAST;
377 h->control = 0;
378
379 switch (pep->af) {
380 case AF_INET: /* Internet Protocol */
381 h->protocol = htons(ETHERTYPE_IP);
382 break;
383 case AF_INET6:
384 h->protocol = htons(ETHERTYPE_IPV6);
385 break;
386 case AF_APPLETALK: /* AppleTalk Protocol */
387 h->protocol = htons(ETHERTYPE_AT);
388 break;
389 case AF_IPX: /* Novell IPX Protocol */
390 h->protocol = htons(ETHERTYPE_IPX);
391 break;
392 default:
393 error = EAFNOSUPPORT;
394 goto out;
395 }
396
397 /* Send it */
398 NG_SEND_DATA(error, sc->downstream.hook, m, meta);
399 return (error);
400
401out:
402 NG_FREE_DATA(m, meta);
403 return (error);
404}
405
406/*
407 * Shutdown node
408 */
409static int
410cisco_rmnode(node_p node)
411{
412 const sc_p sc = node->private;
413
414 node->flags |= NG_INVALID;
415 ng_cutlinks(node);
416 ng_unname(node);
417 node->private = NULL;
418 ng_unref(sc->node);
419 FREE(sc, M_NETGRAPH);
420 return (0);
421}
422
423/*
424 * Disconnection of a hook
425 *
426 * For this type, removal of the last link destroys the node
427 */
428static int
429cisco_disconnect(hook_p hook)
430{
431 const sc_p sc = hook->node->private;
432 struct protoent *pep;
433
434 /* Check it's not the debug hook */
435 if ((pep = hook->private)) {
436 pep->hook = NULL;
437 if (pep->af == 0xffff) {
438 /* If it is the downstream hook, stop the timers */
439 untimeout(cisco_keepalive, sc, sc->handle);
440 }
441 }
442
443 /* If no more hooks, remove the node */
444 if (hook->node->numhooks == 0)
445 ng_rmnode(hook->node);
446 return (0);
447}
448
449/*
450 * Receive data
451 */
452static int
453cisco_input(sc_p sc, struct mbuf *m, meta_p meta)
454{
455 struct cisco_header *h;
456 struct cisco_packet *p;
457 struct protoent *pep;
458 int error = 0;
459
460 if (m->m_pkthdr.len <= CISCO_HEADER_LEN)
461 goto drop;
462
463 /* Strip off cisco header */
464 h = mtod(m, struct cisco_header *);
465 m_adj(m, CISCO_HEADER_LEN);
466
467 switch (h->address) {
468 default: /* Invalid Cisco packet. */
469 goto drop;
470 case CISCO_UNICAST:
471 case CISCO_MULTICAST:
472 /* Don't check the control field here (RFC 1547). */
473 switch (ntohs(h->protocol)) {
474 default:
475 goto drop;
476 case CISCO_KEEPALIVE:
477 p = mtod(m, struct cisco_packet *);
478 switch (ntohl(p->type)) {
479 default:
480 log(LOG_WARNING,
481 "cisco: unknown cisco packet type: 0x%lx\n",
482 ntohl(p->type));
483 break;
484 case CISCO_ADDR_REPLY:
485 /* Reply on address request, ignore */
486 break;
487 case CISCO_KEEPALIVE_REQ:
488 sc->remote_seq = ntohl(p->par1);
489 if (sc->local_seq == ntohl(p->par2)) {
490 sc->local_seq++;
491 sc->seqRetries = 0;
492 }
493 break;
494 case CISCO_ADDR_REQ:
495 {
496 struct ng_mesg *msg, *resp;
497
498 /* Ask inet peer for IP address information */
499 if (sc->inet.hook == NULL)
500 goto nomsg;
501 NG_MKMESSAGE(msg, NGM_CISCO_COOKIE,
502 NGM_CISCO_GET_IPADDR, 0, M_NOWAIT);
503 if (msg == NULL)
504 goto nomsg;
505 ng_send_msg(sc->node, msg,
506 NG_CISCO_HOOK_INET, &resp);
507 if (resp != NULL)
508 cisco_rcvmsg(sc->node, resp, ".",
509 NULL, NULL);
510
511 nomsg:
512 /* Send reply to peer device */
513 error = cisco_send(sc, CISCO_ADDR_REPLY,
514 ntohl(sc->localip.s_addr),
515 ntohl(sc->localmask.s_addr));
516 break;
517 }
518 }
519 goto drop;
520 case ETHERTYPE_IP:
521 pep = &sc->inet;
522 break;
523 case ETHERTYPE_IPV6:
524 pep = &sc->inet6;
525 break;
526 case ETHERTYPE_AT:
527 pep = &sc->atalk;
528 break;
529 case ETHERTYPE_IPX:
530 pep = &sc->ipx;
531 break;
532 }
533 break;
534 }
535
536 /* Send it on */
537 if (pep->hook == NULL)
538 goto drop;
539 NG_SEND_DATA(error, pep->hook, m, meta);
540 return (error);
541
542drop:
543 NG_FREE_DATA(m, meta);
544 return (error);
545}
546
547
548/*
549 * Send keepalive packets, every 10 seconds.
550 */
551static void
552cisco_keepalive(void *arg)
553{
554 const sc_p sc = arg;
555 int s = splimp();
556
557 cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq);
558 sc->seqRetries++;
559 splx(s);
560 sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS);
561}
562
563/*
564 * Send Cisco keepalive packet.
565 */
566static int
567cisco_send(sc_p sc, int type, long par1, long par2)
568{
569 struct cisco_header *h;
570 struct cisco_packet *ch;
571 struct mbuf *m;
572 u_long t;
573 int error = 0;
574 meta_p meta = NULL;
575 struct timeval time;
576
577 getmicrotime(&time);
578
579 MGETHDR(m, M_DONTWAIT, MT_DATA);
580 if (!m)
581 return (ENOBUFS);
582
583 t = (time.tv_sec - boottime.tv_sec) * 1000;
584 m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN;
585 m->m_pkthdr.rcvif = 0;
586
587 h = mtod(m, struct cisco_header *);
588 h->address = CISCO_MULTICAST;
589 h->control = 0;
590 h->protocol = htons(CISCO_KEEPALIVE);
591
592 ch = (struct cisco_packet *) (h + 1);
593 ch->type = htonl(type);
594 ch->par1 = htonl(par1);
595 ch->par2 = htonl(par2);
596 ch->rel = -1;
597 ch->time0 = htons((u_short) (t >> 16));
598 ch->time1 = htons((u_short) t);
599
600 NG_SEND_DATA(error, sc->downstream.hook, m, meta);
601 return (error);
602}