152419Sjulian/*
252419Sjulian * ng_cisco.c
3139823Simp */
4139823Simp
5139823Simp/*-
652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
752419Sjulian * All rights reserved.
852419Sjulian *
952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1052419Sjulian * redistribution of this software, in source or object code forms, with or
1152419Sjulian * without modifications are expressly permitted by Whistle Communications;
1252419Sjulian * provided, however, that:
1352419Sjulian * 1. Any and all reproductions of the source or object code must include the
1452419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1652419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1752419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1852419Sjulian *    such appears in the above copyright notice or in the software.
1952419Sjulian *
2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3652419Sjulian * OF SUCH DAMAGE.
3752419Sjulian *
3867506Sjulian * Author: Julian Elischer <julian@freebsd.org>
3952419Sjulian *
4052419Sjulian * $FreeBSD$
4152752Sjulian * $Whistle: ng_cisco.c,v 1.25 1999/11/01 09:24:51 julian Exp $
4252419Sjulian */
4352419Sjulian
4452419Sjulian#include <sys/param.h>
4552419Sjulian#include <sys/systm.h>
4652419Sjulian#include <sys/errno.h>
4752419Sjulian#include <sys/kernel.h>
4852419Sjulian#include <sys/socket.h>
4952419Sjulian#include <sys/malloc.h>
5052419Sjulian#include <sys/mbuf.h>
5152419Sjulian#include <sys/syslog.h>
5252419Sjulian
5352419Sjulian#include <net/if.h>
5452419Sjulian
5552419Sjulian#include <netinet/in.h>
5652419Sjulian#include <netinet/if_ether.h>
5752419Sjulian
5852419Sjulian#include <netatalk/at.h>
5952419Sjulian
6052419Sjulian#include <netipx/ipx.h>
6152419Sjulian#include <netipx/ipx_if.h>
6252419Sjulian
6352419Sjulian#include <netgraph/ng_message.h>
6452419Sjulian#include <netgraph/netgraph.h>
6553913Sarchie#include <netgraph/ng_parse.h>
6652419Sjulian#include <netgraph/ng_cisco.h>
6752419Sjulian
68137114Sglebius#define	CISCO_MULTICAST		0x8f	/* Cisco multicast address */
69137114Sglebius#define	CISCO_UNICAST		0x0f	/* Cisco unicast address */
70137114Sglebius#define	CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
71137114Sglebius#define	CISCO_ADDR_REQ		0	/* Cisco address request */
72137114Sglebius#define	CISCO_ADDR_REPLY	1	/* Cisco address reply */
73137114Sglebius#define	CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
7452419Sjulian
7552419Sjulian#define KEEPALIVE_SECS		10
7652419Sjulian
7752419Sjulianstruct cisco_header {
78231752Sfjoe	uint8_t  address;
79231752Sfjoe	uint8_t  control;
80231752Sfjoe	uint16_t protocol;
81231752Sfjoe} __packed;
8252419Sjulian
83137114Sglebius#define	CISCO_HEADER_LEN	sizeof (struct cisco_header)
8452419Sjulian
8552419Sjulianstruct cisco_packet {
86231752Sfjoe	uint32_t type;
87231752Sfjoe	uint32_t par1;
88231752Sfjoe	uint32_t par2;
89231752Sfjoe	uint16_t rel;
90231752Sfjoe	uint16_t time0;
91231752Sfjoe	uint16_t time1;
92231752Sfjoe} __packed;
9352419Sjulian
94137114Sglebius#define	CISCO_PACKET_LEN (sizeof(struct cisco_packet))
9552419Sjulian
9652419Sjulianstruct protoent {
9752419Sjulian	hook_p  hook;		/* the hook for this proto */
98231752Sfjoe	uint16_t af;		/* address family, -1 = downstream */
9952419Sjulian};
10052419Sjulian
10152419Sjulianstruct cisco_priv {
102231752Sfjoe	uint32_t local_seq;
103231752Sfjoe	uint32_t remote_seq;
104231752Sfjoe	uint32_t seqRetries;	/* how many times we've been here throwing out
10552419Sjulian				 * the same sequence number without ack */
10652419Sjulian	node_p  node;
107138009Sglebius	struct callout handle;
10852419Sjulian	struct protoent downstream;
10952419Sjulian	struct protoent inet;		/* IP information */
11052419Sjulian	struct in_addr localip;
11152419Sjulian	struct in_addr localmask;
11260330Sarchie	struct protoent inet6;		/* IPv6 information */
11352419Sjulian	struct protoent atalk;		/* AppleTalk information */
11452419Sjulian	struct protoent ipx;		/* IPX information */
11552419Sjulian};
11652419Sjuliantypedef struct cisco_priv *sc_p;
11752419Sjulian
11852419Sjulian/* Netgraph methods */
11952752Sjulianstatic ng_constructor_t		cisco_constructor;
12052752Sjulianstatic ng_rcvmsg_t		cisco_rcvmsg;
12170700Sjulianstatic ng_shutdown_t		cisco_shutdown;
12252752Sjulianstatic ng_newhook_t		cisco_newhook;
12352752Sjulianstatic ng_rcvdata_t		cisco_rcvdata;
12452752Sjulianstatic ng_disconnect_t		cisco_disconnect;
12552419Sjulian
12652419Sjulian/* Other functions */
12770700Sjulianstatic int	cisco_input(sc_p sc, item_p item);
128138009Sglebiusstatic void	cisco_keepalive(node_p node, hook_p hook, void *arg1, int arg2);
12952419Sjulianstatic int	cisco_send(sc_p sc, int type, long par1, long par2);
130138009Sglebiusstatic void	cisco_notify(sc_p sc, uint32_t cmd);
13152419Sjulian
13253913Sarchie/* Parse type for struct ng_cisco_ipaddr */
13397685Sarchiestatic const struct ng_parse_struct_field ng_cisco_ipaddr_type_fields[]
13497685Sarchie	= NG_CISCO_IPADDR_TYPE_INFO;
13553913Sarchiestatic const struct ng_parse_type ng_cisco_ipaddr_type = {
13653913Sarchie	&ng_parse_struct_type,
13797685Sarchie	&ng_cisco_ipaddr_type_fields
13853913Sarchie};
13953913Sarchie
14053913Sarchie/* Parse type for struct ng_async_stat */
14197685Sarchiestatic const struct ng_parse_struct_field ng_cisco_stats_type_fields[]
14297685Sarchie	= NG_CISCO_STATS_TYPE_INFO;
14353913Sarchiestatic const struct ng_parse_type ng_cisco_stats_type = {
14453913Sarchie	&ng_parse_struct_type,
14597685Sarchie	&ng_cisco_stats_type_fields
14653913Sarchie};
14753913Sarchie
14853913Sarchie/* List of commands and how to convert arguments to/from ASCII */
14953913Sarchiestatic const struct ng_cmdlist ng_cisco_cmdlist[] = {
15053913Sarchie	{
15153913Sarchie	  NGM_CISCO_COOKIE,
15253913Sarchie	  NGM_CISCO_SET_IPADDR,
15353913Sarchie	  "setipaddr",
15453913Sarchie	  &ng_cisco_ipaddr_type,
15553913Sarchie	  NULL
15653913Sarchie	},
15753913Sarchie	{
15853913Sarchie	  NGM_CISCO_COOKIE,
15953913Sarchie	  NGM_CISCO_GET_IPADDR,
16053913Sarchie	  "getipaddr",
16153913Sarchie	  NULL,
16253913Sarchie	  &ng_cisco_ipaddr_type
16353913Sarchie	},
16453913Sarchie	{
16553913Sarchie	  NGM_CISCO_COOKIE,
16653913Sarchie	  NGM_CISCO_GET_STATUS,
16753913Sarchie	  "getstats",
16853913Sarchie	  NULL,
16953913Sarchie	  &ng_cisco_stats_type
17053913Sarchie	},
17153913Sarchie	{ 0 }
17253913Sarchie};
17353913Sarchie
17452419Sjulian/* Node type */
17552419Sjulianstatic struct ng_type typestruct = {
176129823Sjulian	.version =	NG_ABI_VERSION,
177129823Sjulian	.name =		NG_CISCO_NODE_TYPE,
178129823Sjulian	.constructor =	cisco_constructor,
179129823Sjulian	.rcvmsg =	cisco_rcvmsg,
180129823Sjulian	.shutdown =	cisco_shutdown,
181129823Sjulian	.newhook =	cisco_newhook,
182129823Sjulian	.rcvdata =	cisco_rcvdata,
183129823Sjulian	.disconnect =	cisco_disconnect,
184129823Sjulian	.cmdlist =	ng_cisco_cmdlist,
18552419Sjulian};
18652419SjulianNETGRAPH_INIT(cisco, &typestruct);
18752419Sjulian
18852419Sjulian/*
18952419Sjulian * Node constructor
19052419Sjulian */
19152419Sjulianstatic int
19270700Sjuliancisco_constructor(node_p node)
19352419Sjulian{
19452419Sjulian	sc_p sc;
19552419Sjulian
196220768Sglebius	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
19752419Sjulian
198138009Sglebius	ng_callout_init(&sc->handle);
19970784Sjulian	NG_NODE_SET_PRIVATE(node, sc);
20070700Sjulian	sc->node = node;
20152419Sjulian
20252419Sjulian	/* Initialise the varous protocol hook holders */
20352419Sjulian	sc->downstream.af = 0xffff;
20452419Sjulian	sc->inet.af = AF_INET;
20560330Sarchie	sc->inet6.af = AF_INET6;
20652419Sjulian	sc->atalk.af = AF_APPLETALK;
20752419Sjulian	sc->ipx.af = AF_IPX;
20852419Sjulian	return (0);
20952419Sjulian}
21052419Sjulian
21152419Sjulian/*
21252419Sjulian * Check new hook
21352419Sjulian */
21452419Sjulianstatic int
21552419Sjuliancisco_newhook(node_p node, hook_p hook, const char *name)
21652419Sjulian{
21770784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
21852419Sjulian
21952419Sjulian	if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) {
22052419Sjulian		sc->downstream.hook = hook;
22170784Sjulian		NG_HOOK_SET_PRIVATE(hook, &sc->downstream);
22252419Sjulian
22352419Sjulian		/* Start keepalives */
224138268Sglebius		ng_callout(&sc->handle, node, NULL, (hz * KEEPALIVE_SECS),
225138009Sglebius		    &cisco_keepalive, (void *)sc, 0);
22652419Sjulian	} else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) {
22752419Sjulian		sc->inet.hook = hook;
22870784Sjulian		NG_HOOK_SET_PRIVATE(hook, &sc->inet);
229174118Sjulian	} else if (strcmp(name, NG_CISCO_HOOK_INET6) == 0) {
230174118Sjulian		sc->inet6.hook = hook;
231174118Sjulian		NG_HOOK_SET_PRIVATE(hook, &sc->inet6);
23252419Sjulian	} else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) {
23352419Sjulian		sc->atalk.hook = hook;
23470784Sjulian		NG_HOOK_SET_PRIVATE(hook, &sc->atalk);
23552419Sjulian	} else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) {
23652419Sjulian		sc->ipx.hook = hook;
23770784Sjulian		NG_HOOK_SET_PRIVATE(hook, &sc->ipx);
23852419Sjulian	} else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) {
23970784Sjulian		NG_HOOK_SET_PRIVATE(hook, NULL);	/* unimplemented */
24052419Sjulian	} else
24152419Sjulian		return (EINVAL);
24252419Sjulian	return 0;
24352419Sjulian}
24452419Sjulian
24552419Sjulian/*
24652419Sjulian * Receive control message.
24752419Sjulian */
24852419Sjulianstatic int
24970700Sjuliancisco_rcvmsg(node_p node, item_p item, hook_p lasthook)
25052419Sjulian{
25170700Sjulian	struct ng_mesg *msg;
25270784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
25352419Sjulian	struct ng_mesg *resp = NULL;
25452419Sjulian	int error = 0;
25552419Sjulian
25670700Sjulian	NGI_GET_MSG(item, msg);
25752419Sjulian	switch (msg->header.typecookie) {
25852419Sjulian	case NGM_GENERIC_COOKIE:
25952419Sjulian		switch (msg->header.cmd) {
26052419Sjulian		case NGM_TEXT_STATUS:
26152419Sjulian		    {
26252419Sjulian			char *arg;
26352419Sjulian			int pos;
26452419Sjulian
265145015Sglebius			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
26652419Sjulian			if (resp == NULL) {
26752419Sjulian				error = ENOMEM;
26852419Sjulian				break;
26952419Sjulian			}
27052419Sjulian			arg = (char *) resp->data;
27152419Sjulian			pos = sprintf(arg,
27252419Sjulian			  "keepalive period: %d sec; ", KEEPALIVE_SECS);
27352419Sjulian			pos += sprintf(arg + pos,
274231752Sfjoe			  "unacknowledged keepalives: %d", sc->seqRetries);
27552419Sjulian			resp->header.arglen = pos + 1;
27652419Sjulian			break;
27752419Sjulian		    }
27852419Sjulian		default:
27952419Sjulian			error = EINVAL;
28052419Sjulian			break;
28152419Sjulian		}
28252419Sjulian		break;
28352419Sjulian	case NGM_CISCO_COOKIE:
28452419Sjulian		switch (msg->header.cmd) {
28552419Sjulian		case NGM_CISCO_GET_IPADDR:	/* could be a late reply! */
28652419Sjulian			if ((msg->header.flags & NGF_RESP) == 0) {
28752419Sjulian				struct in_addr *ips;
28852419Sjulian
28952419Sjulian				NG_MKRESPONSE(resp, msg,
29052419Sjulian				    2 * sizeof(*ips), M_NOWAIT);
29152419Sjulian				if (!resp) {
29252419Sjulian					error = ENOMEM;
29352419Sjulian					break;
29452419Sjulian				}
29552419Sjulian				ips = (struct in_addr *) resp->data;
29652419Sjulian				ips[0] = sc->localip;
29752419Sjulian				ips[1] = sc->localmask;
29852419Sjulian				break;
29952419Sjulian			}
30052419Sjulian			/* FALLTHROUGH */	/* ...if it's a reply */
30152419Sjulian		case NGM_CISCO_SET_IPADDR:
30252419Sjulian		    {
30352419Sjulian			struct in_addr *const ips = (struct in_addr *)msg->data;
30452419Sjulian
30552419Sjulian			if (msg->header.arglen < 2 * sizeof(*ips)) {
30652419Sjulian				error = EINVAL;
30752419Sjulian				break;
30852419Sjulian			}
30952419Sjulian			sc->localip = ips[0];
31052419Sjulian			sc->localmask = ips[1];
31152419Sjulian			break;
31252419Sjulian		    }
31352419Sjulian		case NGM_CISCO_GET_STATUS:
31452419Sjulian		    {
31553913Sarchie			struct ng_cisco_stats *stat;
31652419Sjulian
31752419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
31852419Sjulian			if (!resp) {
31952419Sjulian				error = ENOMEM;
32052419Sjulian				break;
32152419Sjulian			}
32253913Sarchie			stat = (struct ng_cisco_stats *)resp->data;
32353913Sarchie			stat->seqRetries = sc->seqRetries;
32453913Sarchie			stat->keepAlivePeriod = KEEPALIVE_SECS;
32552419Sjulian			break;
32652419Sjulian		    }
32752419Sjulian		default:
32852419Sjulian			error = EINVAL;
32952419Sjulian			break;
33052419Sjulian		}
33152419Sjulian		break;
33252419Sjulian	default:
33352419Sjulian		error = EINVAL;
33452419Sjulian		break;
33552419Sjulian	}
33670700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
33770700Sjulian	NG_FREE_MSG(msg);
33852419Sjulian	return (error);
33952419Sjulian}
34052419Sjulian
34152419Sjulian/*
34252419Sjulian * Receive data
34352419Sjulian */
34452419Sjulianstatic int
34570700Sjuliancisco_rcvdata(hook_p hook, item_p item)
34652419Sjulian{
34770784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
34852419Sjulian	struct protoent *pep;
34952419Sjulian	struct cisco_header *h;
350137114Sglebius	struct mbuf *m;
35152419Sjulian	int error = 0;
35252419Sjulian
35370784Sjulian	if ((pep = NG_HOOK_PRIVATE(hook)) == NULL)
35452419Sjulian		goto out;
35552419Sjulian
35652419Sjulian	/* If it came from our downlink, deal with it separately */
35752419Sjulian	if (pep->af == 0xffff)
35870700Sjulian		return (cisco_input(sc, item));
35952419Sjulian
36052419Sjulian	/* OK so it came from a protocol, heading out. Prepend general data
36152419Sjulian	   packet header. For now, IP,IPX only  */
36270700Sjulian	m = NGI_M(item); /* still associated with item */
363111119Simp	M_PREPEND(m, CISCO_HEADER_LEN, M_DONTWAIT);
36452419Sjulian	if (!m) {
36552419Sjulian		error = ENOBUFS;
36652419Sjulian		goto out;
36752419Sjulian	}
36852419Sjulian	h = mtod(m, struct cisco_header *);
36958171Sphk	h->address = CISCO_UNICAST;
37052419Sjulian	h->control = 0;
37152419Sjulian
37252419Sjulian	switch (pep->af) {
37352419Sjulian	case AF_INET:		/* Internet Protocol */
37452419Sjulian		h->protocol = htons(ETHERTYPE_IP);
37552419Sjulian		break;
37660330Sarchie	case AF_INET6:
37760330Sarchie		h->protocol = htons(ETHERTYPE_IPV6);
37860330Sarchie		break;
37952419Sjulian	case AF_APPLETALK:	/* AppleTalk Protocol */
38052419Sjulian		h->protocol = htons(ETHERTYPE_AT);
38152419Sjulian		break;
38252419Sjulian	case AF_IPX:		/* Novell IPX Protocol */
38352419Sjulian		h->protocol = htons(ETHERTYPE_IPX);
38452419Sjulian		break;
38552419Sjulian	default:
38652419Sjulian		error = EAFNOSUPPORT;
38752419Sjulian		goto out;
38852419Sjulian	}
38952419Sjulian
39052419Sjulian	/* Send it */
39170700Sjulian	NG_FWD_NEW_DATA(error, item,  sc->downstream.hook, m);
39252419Sjulian	return (error);
39352419Sjulian
39452419Sjulianout:
39570700Sjulian	NG_FREE_ITEM(item);
39652419Sjulian	return (error);
39752419Sjulian}
39852419Sjulian
39952419Sjulian/*
40052419Sjulian * Shutdown node
40152419Sjulian */
40252419Sjulianstatic int
40370700Sjuliancisco_shutdown(node_p node)
40452419Sjulian{
40570784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
40652419Sjulian
40770784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
40870784Sjulian	NG_NODE_UNREF(sc->node);
409184205Sdes	free(sc, M_NETGRAPH);
41052419Sjulian	return (0);
41152419Sjulian}
41252419Sjulian
41352419Sjulian/*
41452419Sjulian * Disconnection of a hook
41552419Sjulian *
41652419Sjulian * For this type, removal of the last link destroys the node
41752419Sjulian */
41852419Sjulianstatic int
41952419Sjuliancisco_disconnect(hook_p hook)
42052419Sjulian{
42170784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
42252419Sjulian	struct protoent *pep;
42352419Sjulian
42452419Sjulian	/* Check it's not the debug hook */
42570784Sjulian	if ((pep = NG_HOOK_PRIVATE(hook))) {
42652419Sjulian		pep->hook = NULL;
427138009Sglebius		if (pep->af == 0xffff)
42852419Sjulian			/* If it is the downstream hook, stop the timers */
429138268Sglebius			ng_uncallout(&sc->handle, NG_HOOK_NODE(hook));
43052419Sjulian	}
43152419Sjulian
43252419Sjulian	/* If no more hooks, remove the node */
43370784Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
43470784Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
43570784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
43652419Sjulian	return (0);
43752419Sjulian}
43852419Sjulian
43952419Sjulian/*
44052419Sjulian * Receive data
44152419Sjulian */
44252419Sjulianstatic int
44370700Sjuliancisco_input(sc_p sc, item_p item)
44452419Sjulian{
44597895Sarchie	const struct cisco_header *h;
44697895Sarchie	struct cisco_header hdrbuf;
44752419Sjulian	struct protoent *pep;
448137114Sglebius	struct mbuf *m;
44952419Sjulian	int error = 0;
45052419Sjulian
45197895Sarchie	/* Get data */
45270700Sjulian	m = NGI_M(item);
45397895Sarchie
45497895Sarchie	/* Sanity check header length */
45597895Sarchie	if (m->m_pkthdr.len < sizeof(*h)) {
45697895Sarchie		error = EINVAL;
45752419Sjulian		goto drop;
45897895Sarchie	}
45952419Sjulian
46097895Sarchie	/* Get cisco header */
46197895Sarchie	if (m->m_len >= sizeof(*h))			/* the common case */
46297895Sarchie		h = mtod(m, const struct cisco_header *);
46397895Sarchie	else {
46497895Sarchie		m_copydata(m, 0, sizeof(*h), (caddr_t)&hdrbuf);
46597895Sarchie		h = &hdrbuf;
46697895Sarchie	}
46797895Sarchie	m_adj(m, sizeof(*h));
46852419Sjulian
46997895Sarchie	/* Check header address */
47052419Sjulian	switch (h->address) {
47152419Sjulian	default:		/* Invalid Cisco packet. */
47252419Sjulian		goto drop;
47352419Sjulian	case CISCO_UNICAST:
47452419Sjulian	case CISCO_MULTICAST:
47552419Sjulian		/* Don't check the control field here (RFC 1547). */
47652419Sjulian		switch (ntohs(h->protocol)) {
47752419Sjulian		default:
47852419Sjulian			goto drop;
47952419Sjulian		case CISCO_KEEPALIVE:
48097895Sarchie		    {
48197895Sarchie			const struct cisco_packet *p;
48297895Sarchie			struct cisco_packet pktbuf;
48397895Sarchie
48497895Sarchie			/* Sanity check packet length */
48597895Sarchie			if (m->m_pkthdr.len < sizeof(*p)) {
48697895Sarchie				error = EINVAL;
48797895Sarchie				goto drop;
48897895Sarchie			}
48997895Sarchie
49097895Sarchie			/* Get cisco packet */
49197895Sarchie			if (m->m_len >= sizeof(*p))	/* the common case */
49297895Sarchie				p = mtod(m, const struct cisco_packet *);
49397895Sarchie			else {
49497895Sarchie				m_copydata(m, 0, sizeof(*p), (caddr_t)&pktbuf);
49597895Sarchie				p = &pktbuf;
49697895Sarchie			}
49797895Sarchie
49897895Sarchie			/* Check packet type */
49952419Sjulian			switch (ntohl(p->type)) {
50052419Sjulian			default:
50152419Sjulian				log(LOG_WARNING,
50278252Speter				    "cisco: unknown cisco packet type: 0x%lx\n",
50385659Sdillon				       (long)ntohl(p->type));
50452419Sjulian				break;
50552419Sjulian			case CISCO_ADDR_REPLY:
50652419Sjulian				/* Reply on address request, ignore */
50752419Sjulian				break;
50852419Sjulian			case CISCO_KEEPALIVE_REQ:
50952419Sjulian				sc->remote_seq = ntohl(p->par1);
51052419Sjulian				if (sc->local_seq == ntohl(p->par2)) {
51152419Sjulian					sc->local_seq++;
512138009Sglebius					if (sc->seqRetries > 1)
513138009Sglebius						cisco_notify(sc, NGM_LINK_IS_UP);
51453913Sarchie					sc->seqRetries = 0;
51552419Sjulian				}
51652419Sjulian				break;
51752419Sjulian			case CISCO_ADDR_REQ:
51852419Sjulian			    {
51970700Sjulian				struct ng_mesg *msg;
52070700Sjulian				int dummy_error = 0;
52152419Sjulian
52252419Sjulian				/* Ask inet peer for IP address information */
52352419Sjulian				if (sc->inet.hook == NULL)
52452419Sjulian					goto nomsg;
52552419Sjulian				NG_MKMESSAGE(msg, NGM_CISCO_COOKIE,
52652419Sjulian				    NGM_CISCO_GET_IPADDR, 0, M_NOWAIT);
52752419Sjulian				if (msg == NULL)
52852419Sjulian					goto nomsg;
529102244Sarchie				NG_SEND_MSG_HOOK(dummy_error,
530102244Sarchie				    sc->node, msg, sc->inet.hook, 0);
53170700Sjulian		/*
53270700Sjulian		 * XXX Now maybe we should set a flag telling
53370700Sjulian		 * our receiver to send this message when the response comes in
53470700Sjulian		 * instead of now when the data may be bad.
53570700Sjulian		 */
53652419Sjulian		nomsg:
53752419Sjulian				/* Send reply to peer device */
53852419Sjulian				error = cisco_send(sc, CISCO_ADDR_REPLY,
53952419Sjulian					    ntohl(sc->localip.s_addr),
54052419Sjulian					    ntohl(sc->localmask.s_addr));
54152419Sjulian				break;
54252419Sjulian			    }
54352419Sjulian			}
54452419Sjulian			goto drop;
54597895Sarchie		    }
54652419Sjulian		case ETHERTYPE_IP:
54752419Sjulian			pep = &sc->inet;
54852419Sjulian			break;
54960330Sarchie		case ETHERTYPE_IPV6:
55060330Sarchie			pep = &sc->inet6;
55160330Sarchie			break;
55252419Sjulian		case ETHERTYPE_AT:
55352419Sjulian			pep = &sc->atalk;
55452419Sjulian			break;
55552419Sjulian		case ETHERTYPE_IPX:
55652419Sjulian			pep = &sc->ipx;
55752419Sjulian			break;
55852419Sjulian		}
55952419Sjulian		break;
56052419Sjulian	}
56152419Sjulian
56297895Sarchie	/* Drop if payload is empty */
56397895Sarchie	if (m->m_pkthdr.len == 0) {
56497895Sarchie		error = EINVAL;
56597895Sarchie		goto drop;
56697895Sarchie	}
56797895Sarchie
56852419Sjulian	/* Send it on */
56952419Sjulian	if (pep->hook == NULL)
57052419Sjulian		goto drop;
57170700Sjulian	NG_FWD_NEW_DATA(error, item, pep->hook, m);
57252419Sjulian	return (error);
57352419Sjulian
57452419Sjuliandrop:
57570700Sjulian	NG_FREE_ITEM(item);
57652419Sjulian	return (error);
57752419Sjulian}
57852419Sjulian
57952419Sjulian
58052419Sjulian/*
58152419Sjulian * Send keepalive packets, every 10 seconds.
58252419Sjulian */
58352419Sjulianstatic void
584138009Sglebiuscisco_keepalive(node_p node, hook_p hook, void *arg1, int arg2)
58552419Sjulian{
586138009Sglebius	const sc_p sc = arg1;
58752419Sjulian
58852419Sjulian	cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq);
589138009Sglebius	if (sc->seqRetries++ > 1)
590138009Sglebius		cisco_notify(sc, NGM_LINK_IS_DOWN);
591138268Sglebius	ng_callout(&sc->handle, node, NULL, (hz * KEEPALIVE_SECS),
592138009Sglebius	    &cisco_keepalive, (void *)sc, 0);
59352419Sjulian}
59452419Sjulian
59552419Sjulian/*
59652419Sjulian * Send Cisco keepalive packet.
59752419Sjulian */
59852419Sjulianstatic int
59952419Sjuliancisco_send(sc_p sc, int type, long par1, long par2)
60052419Sjulian{
60152419Sjulian	struct cisco_header *h;
60252419Sjulian	struct cisco_packet *ch;
60352419Sjulian	struct mbuf *m;
604137114Sglebius	struct timeval time;
605231752Sfjoe	uint32_t t;
60652419Sjulian	int     error = 0;
60752419Sjulian
608124810Sphk	getmicrouptime(&time);
60952419Sjulian
610111119Simp	MGETHDR(m, M_DONTWAIT, MT_DATA);
61152419Sjulian	if (!m)
61252419Sjulian		return (ENOBUFS);
61352419Sjulian
614124810Sphk	t = time.tv_sec * 1000 + time.tv_usec / 1000;
61552419Sjulian	m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN;
61652419Sjulian	m->m_pkthdr.rcvif = 0;
61752419Sjulian
61852419Sjulian	h = mtod(m, struct cisco_header *);
61952419Sjulian	h->address = CISCO_MULTICAST;
62052419Sjulian	h->control = 0;
62152419Sjulian	h->protocol = htons(CISCO_KEEPALIVE);
62252419Sjulian
62352419Sjulian	ch = (struct cisco_packet *) (h + 1);
62452419Sjulian	ch->type = htonl(type);
62552419Sjulian	ch->par1 = htonl(par1);
62652419Sjulian	ch->par2 = htonl(par2);
62752419Sjulian	ch->rel = -1;
628231752Sfjoe	ch->time0 = htons((uint16_t) (t >> 16));
629231752Sfjoe	ch->time1 = htons((uint16_t) t);
63052419Sjulian
63170700Sjulian	NG_SEND_DATA_ONLY(error, sc->downstream.hook, m);
63252419Sjulian	return (error);
63352419Sjulian}
634138009Sglebius
635138009Sglebius/*
636138009Sglebius * Send linkstate to upstream node.
637138009Sglebius */
638138009Sglebiusstatic void
639138009Sglebiuscisco_notify(sc_p sc, uint32_t cmd)
640138009Sglebius{
641138009Sglebius	struct ng_mesg *msg;
642138009Sglebius	int dummy_error = 0;
643138009Sglebius
644138009Sglebius	if (sc->inet.hook == NULL) /* nothing to notify */
645138009Sglebius		return;
646138009Sglebius
647138009Sglebius	NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
648138009Sglebius	if (msg != NULL)
649138009Sglebius		NG_SEND_MSG_HOOK(dummy_error, sc->node, msg, sc->inet.hook, 0);
650138009Sglebius}
651