154359Sroberto/*
254359Sroberto * ng_cisco.c
354359Sroberto */
454359Sroberto
554359Sroberto/*-
654359Sroberto * Copyright (c) 1996-1999 Whistle Communications, Inc.
754359Sroberto * All rights reserved.
854359Sroberto *
954359Sroberto * Subject to the following obligations and disclaimer of warranty, use and
1054359Sroberto * redistribution of this software, in source or object code forms, with or
1154359Sroberto * without modifications are expressly permitted by Whistle Communications;
1254359Sroberto * provided, however, that:
1354359Sroberto * 1. Any and all reproductions of the source or object code must include the
1454359Sroberto *    copyright notice above and the following disclaimer of warranties; and
1554359Sroberto * 2. No rights are granted, in any manner or form, to use Whistle
1682498Sroberto *    Communications, Inc. trademarks, including the mark "WHISTLE
1782498Sroberto *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1882498Sroberto *    such appears in the above copyright notice or in the software.
1954359Sroberto *
2054359Sroberto * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2154359Sroberto * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2254359Sroberto * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2354359Sroberto * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24285612Sdelphij * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25285612Sdelphij * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26285612Sdelphij * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2754359Sroberto * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28285612Sdelphij * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29285612Sdelphij * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3054359Sroberto * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3154359Sroberto * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3254359Sroberto * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3354359Sroberto * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3454359Sroberto * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3554359Sroberto * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3654359Sroberto * OF SUCH DAMAGE.
3754359Sroberto *
3854359Sroberto * Author: Julian Elischer <julian@freebsd.org>
3954359Sroberto * $Whistle: ng_cisco.c,v 1.25 1999/11/01 09:24:51 julian Exp $
4054359Sroberto */
4154359Sroberto
4254359Sroberto#include <sys/param.h>
4354359Sroberto#include <sys/systm.h>
4454359Sroberto#include <sys/errno.h>
4554359Sroberto#include <sys/kernel.h>
4654359Sroberto#include <sys/socket.h>
4754359Sroberto#include <sys/malloc.h>
4854359Sroberto#include <sys/mbuf.h>
4954359Sroberto#include <sys/syslog.h>
5054359Sroberto
5154359Sroberto#include <net/if.h>
5254359Sroberto
5354359Sroberto#include <netinet/in.h>
5454359Sroberto#include <netinet/if_ether.h>
5554359Sroberto
5654359Sroberto#include <netgraph/ng_message.h>
5754359Sroberto#include <netgraph/netgraph.h>
5854359Sroberto#include <netgraph/ng_parse.h>
5954359Sroberto#include <netgraph/ng_cisco.h>
6054359Sroberto
6154359Sroberto#define	CISCO_MULTICAST		0x8f	/* Cisco multicast address */
6254359Sroberto#define	CISCO_UNICAST		0x0f	/* Cisco unicast address */
63285612Sdelphij#define	CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
64285612Sdelphij#define	CISCO_ADDR_REQ		0	/* Cisco address request */
65285612Sdelphij#define	CISCO_ADDR_REPLY	1	/* Cisco address reply */
6654359Sroberto#define	CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
6754359Sroberto
6854359Sroberto#define KEEPALIVE_SECS		10
6954359Sroberto
7054359Srobertostruct cisco_header {
7154359Sroberto	uint8_t  address;
7254359Sroberto	uint8_t  control;
7354359Sroberto	uint16_t protocol;
7454359Sroberto} __packed;
7554359Sroberto
7654359Sroberto#define	CISCO_HEADER_LEN	sizeof (struct cisco_header)
7754359Sroberto
7854359Srobertostruct cisco_packet {
7954359Sroberto	uint32_t type;
8054359Sroberto	uint32_t par1;
8154359Sroberto	uint32_t par2;
8254359Sroberto	uint16_t rel;
8354359Sroberto	uint16_t time0;
8454359Sroberto	uint16_t time1;
8554359Sroberto} __packed;
8654359Sroberto
8754359Sroberto#define	CISCO_PACKET_LEN (sizeof(struct cisco_packet))
8854359Sroberto
8954359Srobertostruct protoent {
9054359Sroberto	hook_p  hook;		/* the hook for this proto */
9154359Sroberto	uint16_t af;		/* address family, -1 = downstream */
9254359Sroberto};
9354359Sroberto
9454359Srobertostruct cisco_priv {
9554359Sroberto	uint32_t local_seq;
9654359Sroberto	uint32_t remote_seq;
97285612Sdelphij	uint32_t seqRetries;	/* how many times we've been here throwing out
9854359Sroberto				 * the same sequence number without ack */
9954359Sroberto	node_p  node;
10054359Sroberto	struct callout handle;
10154359Sroberto	struct protoent downstream;
10254359Sroberto	struct protoent inet;		/* IP information */
10354359Sroberto	struct in_addr localip;
10454359Sroberto	struct in_addr localmask;
10554359Sroberto	struct protoent inet6;		/* IPv6 information */
10654359Sroberto	struct protoent atalk;		/* AppleTalk information */
10754359Sroberto	struct protoent ipx;		/* IPX information */
10854359Sroberto};
10954359Srobertotypedef struct cisco_priv *sc_p;
11054359Sroberto
11154359Sroberto/* Netgraph methods */
11254359Srobertostatic ng_constructor_t		cisco_constructor;
11354359Srobertostatic ng_rcvmsg_t		cisco_rcvmsg;
11454359Srobertostatic ng_shutdown_t		cisco_shutdown;
11554359Srobertostatic ng_newhook_t		cisco_newhook;
11654359Srobertostatic ng_rcvdata_t		cisco_rcvdata;
11754359Srobertostatic ng_disconnect_t		cisco_disconnect;
11854359Sroberto
11954359Sroberto/* Other functions */
12054359Srobertostatic int	cisco_input(sc_p sc, item_p item);
12154359Srobertostatic void	cisco_keepalive(node_p node, hook_p hook, void *arg1, int arg2);
12254359Srobertostatic int	cisco_send(sc_p sc, int type, long par1, long par2);
12354359Srobertostatic void	cisco_notify(sc_p sc, uint32_t cmd);
12454359Sroberto
12554359Sroberto/* Parse type for struct ng_cisco_ipaddr */
12654359Srobertostatic const struct ng_parse_struct_field ng_cisco_ipaddr_type_fields[]
12754359Sroberto	= NG_CISCO_IPADDR_TYPE_INFO;
12854359Srobertostatic const struct ng_parse_type ng_cisco_ipaddr_type = {
12954359Sroberto	&ng_parse_struct_type,
13054359Sroberto	&ng_cisco_ipaddr_type_fields
13154359Sroberto};
13254359Sroberto
13354359Sroberto/* Parse type for struct ng_async_stat */
13454359Srobertostatic const struct ng_parse_struct_field ng_cisco_stats_type_fields[]
13554359Sroberto	= NG_CISCO_STATS_TYPE_INFO;
13654359Srobertostatic const struct ng_parse_type ng_cisco_stats_type = {
13754359Sroberto	&ng_parse_struct_type,
13854359Sroberto	&ng_cisco_stats_type_fields
13954359Sroberto};
14054359Sroberto
14154359Sroberto/* List of commands and how to convert arguments to/from ASCII */
14254359Srobertostatic const struct ng_cmdlist ng_cisco_cmdlist[] = {
14354359Sroberto	{
14454359Sroberto	  NGM_CISCO_COOKIE,
14554359Sroberto	  NGM_CISCO_SET_IPADDR,
14654359Sroberto	  "setipaddr",
14754359Sroberto	  &ng_cisco_ipaddr_type,
14854359Sroberto	  NULL
14954359Sroberto	},
15054359Sroberto	{
151285612Sdelphij	  NGM_CISCO_COOKIE,
152285612Sdelphij	  NGM_CISCO_GET_IPADDR,
15354359Sroberto	  "getipaddr",
15454359Sroberto	  NULL,
15554359Sroberto	  &ng_cisco_ipaddr_type
15654359Sroberto	},
15754359Sroberto	{
15854359Sroberto	  NGM_CISCO_COOKIE,
15954359Sroberto	  NGM_CISCO_GET_STATUS,
16054359Sroberto	  "getstats",
16154359Sroberto	  NULL,
16254359Sroberto	  &ng_cisco_stats_type
16354359Sroberto	},
16454359Sroberto	{ 0 }
16554359Sroberto};
16654359Sroberto
16754359Sroberto/* Node type */
16854359Srobertostatic struct ng_type typestruct = {
169285612Sdelphij	.version =	NG_ABI_VERSION,
17054359Sroberto	.name =		NG_CISCO_NODE_TYPE,
17154359Sroberto	.constructor =	cisco_constructor,
17254359Sroberto	.rcvmsg =	cisco_rcvmsg,
17354359Sroberto	.shutdown =	cisco_shutdown,
17454359Sroberto	.newhook =	cisco_newhook,
17554359Sroberto	.rcvdata =	cisco_rcvdata,
17654359Sroberto	.disconnect =	cisco_disconnect,
17754359Sroberto	.cmdlist =	ng_cisco_cmdlist,
17854359Sroberto};
17954359SrobertoNETGRAPH_INIT(cisco, &typestruct);
18054359Sroberto
18154359Sroberto/*
18254359Sroberto * Node constructor
18354359Sroberto */
18454359Srobertostatic int
18554359Srobertocisco_constructor(node_p node)
186285612Sdelphij{
187285612Sdelphij	sc_p sc;
188285612Sdelphij
18954359Sroberto	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
19054359Sroberto
191285612Sdelphij	ng_callout_init(&sc->handle);
19254359Sroberto	NG_NODE_SET_PRIVATE(node, sc);
19354359Sroberto	sc->node = node;
19454359Sroberto
19554359Sroberto	/* Initialise the varous protocol hook holders */
19654359Sroberto	sc->downstream.af = 0xffff;
19754359Sroberto	sc->inet.af = AF_INET;
19854359Sroberto	sc->inet6.af = AF_INET6;
19954359Sroberto	sc->atalk.af = AF_APPLETALK;
20054359Sroberto	sc->ipx.af = AF_IPX;
20154359Sroberto	return (0);
20254359Sroberto}
20354359Sroberto
20454359Sroberto/*
20554359Sroberto * Check new hook
20654359Sroberto */
20754359Srobertostatic int
20854359Srobertocisco_newhook(node_p node, hook_p hook, const char *name)
20954359Sroberto{
21054359Sroberto	const sc_p sc = NG_NODE_PRIVATE(node);
21154359Sroberto
21254359Sroberto	if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) {
21354359Sroberto		sc->downstream.hook = hook;
21454359Sroberto		NG_HOOK_SET_PRIVATE(hook, &sc->downstream);
21554359Sroberto
21654359Sroberto		/* Start keepalives */
21754359Sroberto		ng_callout(&sc->handle, node, NULL, (hz * KEEPALIVE_SECS),
21854359Sroberto		    &cisco_keepalive, (void *)sc, 0);
21954359Sroberto	} else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) {
22054359Sroberto		sc->inet.hook = hook;
22154359Sroberto		NG_HOOK_SET_PRIVATE(hook, &sc->inet);
22254359Sroberto	} else if (strcmp(name, NG_CISCO_HOOK_INET6) == 0) {
22354359Sroberto		sc->inet6.hook = hook;
22454359Sroberto		NG_HOOK_SET_PRIVATE(hook, &sc->inet6);
22554359Sroberto	} else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) {
22654359Sroberto		sc->atalk.hook = hook;
22754359Sroberto		NG_HOOK_SET_PRIVATE(hook, &sc->atalk);
22854359Sroberto	} else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) {
22954359Sroberto		sc->ipx.hook = hook;
23054359Sroberto		NG_HOOK_SET_PRIVATE(hook, &sc->ipx);
23154359Sroberto	} else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) {
232285612Sdelphij		NG_HOOK_SET_PRIVATE(hook, NULL);	/* unimplemented */
233285612Sdelphij	} else
234285612Sdelphij		return (EINVAL);
235285612Sdelphij	return 0;
23654359Sroberto}
23754359Sroberto
23854359Sroberto/*
23954359Sroberto * Receive control message.
24054359Sroberto */
24154359Srobertostatic int
24254359Srobertocisco_rcvmsg(node_p node, item_p item, hook_p lasthook)
24354359Sroberto{
24454359Sroberto	struct ng_mesg *msg;
24554359Sroberto	const sc_p sc = NG_NODE_PRIVATE(node);
24654359Sroberto	struct ng_mesg *resp = NULL;
24754359Sroberto	int error = 0;
24854359Sroberto
24954359Sroberto	NGI_GET_MSG(item, msg);
25054359Sroberto	switch (msg->header.typecookie) {
25154359Sroberto	case NGM_GENERIC_COOKIE:
25254359Sroberto		switch (msg->header.cmd) {
25354359Sroberto		case NGM_TEXT_STATUS:
25454359Sroberto		    {
25554359Sroberto			char *arg;
25654359Sroberto			int pos;
25754359Sroberto
25854359Sroberto			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
25954359Sroberto			if (resp == NULL) {
26054359Sroberto				error = ENOMEM;
261285612Sdelphij				break;
262285612Sdelphij			}
26354359Sroberto			arg = (char *) resp->data;
26454359Sroberto			pos = sprintf(arg,
26554359Sroberto			  "keepalive period: %d sec; ", KEEPALIVE_SECS);
26654359Sroberto			pos += sprintf(arg + pos,
26754359Sroberto			  "unacknowledged keepalives: %d", sc->seqRetries);
26854359Sroberto			resp->header.arglen = pos + 1;
26954359Sroberto			break;
27054359Sroberto		    }
27154359Sroberto		default:
27254359Sroberto			error = EINVAL;
27354359Sroberto			break;
27454359Sroberto		}
27554359Sroberto		break;
27654359Sroberto	case NGM_CISCO_COOKIE:
27754359Sroberto		switch (msg->header.cmd) {
27854359Sroberto		case NGM_CISCO_GET_IPADDR:	/* could be a late reply! */
27954359Sroberto			if ((msg->header.flags & NGF_RESP) == 0) {
28054359Sroberto				struct in_addr *ips;
28154359Sroberto
28254359Sroberto				NG_MKRESPONSE(resp, msg,
28354359Sroberto				    2 * sizeof(*ips), M_NOWAIT);
28454359Sroberto				if (!resp) {
28554359Sroberto					error = ENOMEM;
28654359Sroberto					break;
28754359Sroberto				}
28854359Sroberto				ips = (struct in_addr *) resp->data;
28954359Sroberto				ips[0] = sc->localip;
29054359Sroberto				ips[1] = sc->localmask;
29154359Sroberto				break;
29254359Sroberto			}
29354359Sroberto			/* FALLTHROUGH */	/* ...if it's a reply */
29454359Sroberto		case NGM_CISCO_SET_IPADDR:
29554359Sroberto		    {
29654359Sroberto			struct in_addr *const ips = (struct in_addr *)msg->data;
29754359Sroberto
29854359Sroberto			if (msg->header.arglen < 2 * sizeof(*ips)) {
29954359Sroberto				error = EINVAL;
30054359Sroberto				break;
30154359Sroberto			}
30254359Sroberto			sc->localip = ips[0];
30354359Sroberto			sc->localmask = ips[1];
30454359Sroberto			break;
30554359Sroberto		    }
30654359Sroberto		case NGM_CISCO_GET_STATUS:
30754359Sroberto		    {
30854359Sroberto			struct ng_cisco_stats *stat;
30954359Sroberto
31054359Sroberto			NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT);
31154359Sroberto			if (!resp) {
31254359Sroberto				error = ENOMEM;
31354359Sroberto				break;
31454359Sroberto			}
31554359Sroberto			stat = (struct ng_cisco_stats *)resp->data;
31654359Sroberto			stat->seqRetries = sc->seqRetries;
31754359Sroberto			stat->keepAlivePeriod = KEEPALIVE_SECS;
31854359Sroberto			break;
31954359Sroberto		    }
32054359Sroberto		default:
32154359Sroberto			error = EINVAL;
32254359Sroberto			break;
32354359Sroberto		}
32454359Sroberto		break;
32554359Sroberto	default:
32654359Sroberto		error = EINVAL;
32754359Sroberto		break;
32854359Sroberto	}
32954359Sroberto	NG_RESPOND_MSG(error, node, item, resp);
33054359Sroberto	NG_FREE_MSG(msg);
33154359Sroberto	return (error);
33254359Sroberto}
33354359Sroberto
33454359Sroberto/*
33554359Sroberto * Receive data
33654359Sroberto */
33754359Srobertostatic int
33854359Srobertocisco_rcvdata(hook_p hook, item_p item)
33954359Sroberto{
34054359Sroberto	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
34154359Sroberto	struct protoent *pep;
34254359Sroberto	struct cisco_header *h;
34354359Sroberto	struct mbuf *m;
34454359Sroberto	int error = 0;
34554359Sroberto
34654359Sroberto	if ((pep = NG_HOOK_PRIVATE(hook)) == NULL)
34754359Sroberto		goto out;
34854359Sroberto
34954359Sroberto	/* If it came from our downlink, deal with it separately */
35054359Sroberto	if (pep->af == 0xffff)
35154359Sroberto		return (cisco_input(sc, item));
35254359Sroberto
35354359Sroberto	/* OK so it came from a protocol, heading out. Prepend general data
35454359Sroberto	   packet header. For now, IP,IPX only  */
35554359Sroberto	NGI_GET_M(item, m);
35654359Sroberto	M_PREPEND(m, CISCO_HEADER_LEN, M_NOWAIT);
35754359Sroberto	if (!m) {
35854359Sroberto		error = ENOBUFS;
35954359Sroberto		goto out;
36054359Sroberto	}
36154359Sroberto	NGI_M(item) = m;
36254359Sroberto	h = mtod(m, struct cisco_header *);
36354359Sroberto	h->address = CISCO_UNICAST;
36454359Sroberto	h->control = 0;
36554359Sroberto
36654359Sroberto	switch (pep->af) {
36754359Sroberto	case AF_INET:		/* Internet Protocol */
36854359Sroberto		h->protocol = htons(ETHERTYPE_IP);
36954359Sroberto		break;
37054359Sroberto	case AF_INET6:
37154359Sroberto		h->protocol = htons(ETHERTYPE_IPV6);
37254359Sroberto		break;
37354359Sroberto	case AF_APPLETALK:	/* AppleTalk Protocol */
37454359Sroberto		h->protocol = htons(ETHERTYPE_AT);
37554359Sroberto		break;
37654359Sroberto	case AF_IPX:		/* Novell IPX Protocol */
37754359Sroberto		h->protocol = htons(ETHERTYPE_IPX);
37854359Sroberto		break;
37954359Sroberto	default:
38054359Sroberto		error = EAFNOSUPPORT;
38154359Sroberto		goto out;
38254359Sroberto	}
38354359Sroberto
38454359Sroberto	/* Send it */
38554359Sroberto	NG_FWD_NEW_DATA(error, item,  sc->downstream.hook, m);
38654359Sroberto	return (error);
38754359Sroberto
38854359Srobertoout:
38954359Sroberto	NG_FREE_ITEM(item);
39054359Sroberto	return (error);
39154359Sroberto}
39254359Sroberto
39354359Sroberto/*
39454359Sroberto * Shutdown node
39554359Sroberto */
39654359Srobertostatic int
39754359Srobertocisco_shutdown(node_p node)
39854359Sroberto{
39954359Sroberto	const sc_p sc = NG_NODE_PRIVATE(node);
40054359Sroberto
40154359Sroberto	NG_NODE_SET_PRIVATE(node, NULL);
40254359Sroberto	NG_NODE_UNREF(sc->node);
40354359Sroberto	free(sc, M_NETGRAPH);
40454359Sroberto	return (0);
40554359Sroberto}
40654359Sroberto
40754359Sroberto/*
40854359Sroberto * Disconnection of a hook
40954359Sroberto *
41054359Sroberto * For this type, removal of the last link destroys the node
41154359Sroberto */
41254359Srobertostatic int
41354359Srobertocisco_disconnect(hook_p hook)
41454359Sroberto{
41554359Sroberto	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
41654359Sroberto	struct protoent *pep;
41754359Sroberto
41854359Sroberto	/* Check it's not the debug hook */
41954359Sroberto	if ((pep = NG_HOOK_PRIVATE(hook))) {
42054359Sroberto		pep->hook = NULL;
42154359Sroberto		if (pep->af == 0xffff)
42254359Sroberto			/* If it is the downstream hook, stop the timers */
42354359Sroberto			ng_uncallout(&sc->handle, NG_HOOK_NODE(hook));
42454359Sroberto	}
42554359Sroberto
42654359Sroberto	/* If no more hooks, remove the node */
42754359Sroberto	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
42854359Sroberto	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
42954359Sroberto		ng_rmnode_self(NG_HOOK_NODE(hook));
43054359Sroberto	return (0);
43154359Sroberto}
43254359Sroberto
43354359Sroberto/*
43454359Sroberto * Receive data
43554359Sroberto */
43654359Srobertostatic int
43754359Srobertocisco_input(sc_p sc, item_p item)
43854359Sroberto{
43954359Sroberto	const struct cisco_header *h;
44054359Sroberto	struct cisco_header hdrbuf;
44154359Sroberto	struct protoent *pep;
44254359Sroberto	struct mbuf *m;
44354359Sroberto	int error = 0;
44454359Sroberto
44554359Sroberto	/* Get data */
44654359Sroberto	m = NGI_M(item);
44754359Sroberto
44854359Sroberto	/* Sanity check header length */
44954359Sroberto	if (m->m_pkthdr.len < sizeof(*h)) {
45054359Sroberto		error = EINVAL;
45154359Sroberto		goto drop;
45254359Sroberto	}
45354359Sroberto
45454359Sroberto	/* Get cisco header */
45554359Sroberto	if (m->m_len >= sizeof(*h))			/* the common case */
45654359Sroberto		h = mtod(m, const struct cisco_header *);
45754359Sroberto	else {
45854359Sroberto		m_copydata(m, 0, sizeof(*h), (caddr_t)&hdrbuf);
45954359Sroberto		h = &hdrbuf;
46054359Sroberto	}
46154359Sroberto	m_adj(m, sizeof(*h));
46254359Sroberto
46354359Sroberto	/* Check header address */
46454359Sroberto	switch (h->address) {
46554359Sroberto	default:		/* Invalid Cisco packet. */
46654359Sroberto		goto drop;
46754359Sroberto	case CISCO_UNICAST:
46854359Sroberto	case CISCO_MULTICAST:
46954359Sroberto		/* Don't check the control field here (RFC 1547). */
47054359Sroberto		switch (ntohs(h->protocol)) {
47154359Sroberto		default:
47254359Sroberto			goto drop;
47354359Sroberto		case CISCO_KEEPALIVE:
47454359Sroberto		    {
47554359Sroberto			const struct cisco_packet *p;
47654359Sroberto			struct cisco_packet pktbuf;
47754359Sroberto
47854359Sroberto			/* Sanity check packet length */
47954359Sroberto			if (m->m_pkthdr.len < sizeof(*p)) {
48054359Sroberto				error = EINVAL;
48154359Sroberto				goto drop;
48254359Sroberto			}
48354359Sroberto
48454359Sroberto			/* Get cisco packet */
48554359Sroberto			if (m->m_len >= sizeof(*p))	/* the common case */
48654359Sroberto				p = mtod(m, const struct cisco_packet *);
48754359Sroberto			else {
48854359Sroberto				m_copydata(m, 0, sizeof(*p), (caddr_t)&pktbuf);
48954359Sroberto				p = &pktbuf;
49054359Sroberto			}
49154359Sroberto
49254359Sroberto			/* Check packet type */
49354359Sroberto			switch (ntohl(p->type)) {
49454359Sroberto			default:
49554359Sroberto				log(LOG_WARNING,
49654359Sroberto				    "cisco: unknown cisco packet type: 0x%lx\n",
49754359Sroberto				       (long)ntohl(p->type));
49854359Sroberto				break;
49954359Sroberto			case CISCO_ADDR_REPLY:
50054359Sroberto				/* Reply on address request, ignore */
50154359Sroberto				break;
50254359Sroberto			case CISCO_KEEPALIVE_REQ:
50354359Sroberto				sc->remote_seq = ntohl(p->par1);
50454359Sroberto				if (sc->local_seq == ntohl(p->par2)) {
50554359Sroberto					sc->local_seq++;
50654359Sroberto					if (sc->seqRetries > 1)
50754359Sroberto						cisco_notify(sc, NGM_LINK_IS_UP);
50854359Sroberto					sc->seqRetries = 0;
50954359Sroberto				}
51054359Sroberto				break;
51154359Sroberto			case CISCO_ADDR_REQ:
51254359Sroberto			    {
51354359Sroberto				struct ng_mesg *msg;
51454359Sroberto				int dummy_error = 0;
51554359Sroberto
51654359Sroberto				/* Ask inet peer for IP address information */
51754359Sroberto				if (sc->inet.hook == NULL)
51854359Sroberto					goto nomsg;
51954359Sroberto				NG_MKMESSAGE(msg, NGM_CISCO_COOKIE,
52054359Sroberto				    NGM_CISCO_GET_IPADDR, 0, M_NOWAIT);
52154359Sroberto				if (msg == NULL)
52254359Sroberto					goto nomsg;
52354359Sroberto				NG_SEND_MSG_HOOK(dummy_error,
52454359Sroberto				    sc->node, msg, sc->inet.hook, 0);
52554359Sroberto		/*
52654359Sroberto		 * XXX Now maybe we should set a flag telling
52754359Sroberto		 * our receiver to send this message when the response comes in
52854359Sroberto		 * instead of now when the data may be bad.
52954359Sroberto		 */
53054359Sroberto		nomsg:
53154359Sroberto				/* Send reply to peer device */
53254359Sroberto				error = cisco_send(sc, CISCO_ADDR_REPLY,
53354359Sroberto					    ntohl(sc->localip.s_addr),
53454359Sroberto					    ntohl(sc->localmask.s_addr));
53554359Sroberto				break;
53654359Sroberto			    }
53754359Sroberto			}
53854359Sroberto			goto drop;
53954359Sroberto		    }
54054359Sroberto		case ETHERTYPE_IP:
54154359Sroberto			pep = &sc->inet;
54254359Sroberto			break;
54354359Sroberto		case ETHERTYPE_IPV6:
54454359Sroberto			pep = &sc->inet6;
54554359Sroberto			break;
54654359Sroberto		case ETHERTYPE_AT:
54754359Sroberto			pep = &sc->atalk;
54854359Sroberto			break;
54954359Sroberto		case ETHERTYPE_IPX:
55054359Sroberto			pep = &sc->ipx;
55154359Sroberto			break;
55254359Sroberto		}
55354359Sroberto		break;
55454359Sroberto	}
55554359Sroberto
55654359Sroberto	/* Drop if payload is empty */
55754359Sroberto	if (m->m_pkthdr.len == 0) {
55854359Sroberto		error = EINVAL;
55954359Sroberto		goto drop;
56054359Sroberto	}
56154359Sroberto
56254359Sroberto	/* Send it on */
56354359Sroberto	if (pep->hook == NULL)
56454359Sroberto		goto drop;
56554359Sroberto	NG_FWD_NEW_DATA(error, item, pep->hook, m);
56654359Sroberto	return (error);
56754359Sroberto
56854359Srobertodrop:
56954359Sroberto	NG_FREE_ITEM(item);
57054359Sroberto	return (error);
57154359Sroberto}
57254359Sroberto
57354359Sroberto/*
57454359Sroberto * Send keepalive packets, every 10 seconds.
57554359Sroberto */
57654359Srobertostatic void
57754359Srobertocisco_keepalive(node_p node, hook_p hook, void *arg1, int arg2)
57854359Sroberto{
57954359Sroberto	const sc_p sc = arg1;
58054359Sroberto
58154359Sroberto	cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq);
58254359Sroberto	if (sc->seqRetries++ > 1)
58354359Sroberto		cisco_notify(sc, NGM_LINK_IS_DOWN);
58454359Sroberto	ng_callout(&sc->handle, node, NULL, (hz * KEEPALIVE_SECS),
58554359Sroberto	    &cisco_keepalive, (void *)sc, 0);
58654359Sroberto}
58754359Sroberto
58854359Sroberto/*
58954359Sroberto * Send Cisco keepalive packet.
59054359Sroberto */
59154359Srobertostatic int
59254359Srobertocisco_send(sc_p sc, int type, long par1, long par2)
59354359Sroberto{
59454359Sroberto	struct cisco_header *h;
59554359Sroberto	struct cisco_packet *ch;
59654359Sroberto	struct mbuf *m;
59754359Sroberto	struct timeval time;
59854359Sroberto	uint32_t t;
59954359Sroberto	int     error = 0;
60054359Sroberto
60154359Sroberto	getmicrouptime(&time);
60254359Sroberto
60354359Sroberto	MGETHDR(m, M_NOWAIT, MT_DATA);
60454359Sroberto	if (!m)
60554359Sroberto		return (ENOBUFS);
60654359Sroberto
60754359Sroberto	t = time.tv_sec * 1000 + time.tv_usec / 1000;
60854359Sroberto	m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN;
60954359Sroberto	m->m_pkthdr.rcvif = 0;
61054359Sroberto
61154359Sroberto	h = mtod(m, struct cisco_header *);
61254359Sroberto	h->address = CISCO_MULTICAST;
61354359Sroberto	h->control = 0;
61454359Sroberto	h->protocol = htons(CISCO_KEEPALIVE);
61554359Sroberto
61654359Sroberto	ch = (struct cisco_packet *) (h + 1);
61754359Sroberto	ch->type = htonl(type);
61854359Sroberto	ch->par1 = htonl(par1);
61954359Sroberto	ch->par2 = htonl(par2);
62054359Sroberto	ch->rel = -1;
62154359Sroberto	ch->time0 = htons((uint16_t) (t >> 16));
62254359Sroberto	ch->time1 = htons((uint16_t) t);
62354359Sroberto
62454359Sroberto	NG_SEND_DATA_ONLY(error, sc->downstream.hook, m);
62554359Sroberto	return (error);
62654359Sroberto}
62754359Sroberto
62854359Sroberto/*
62954359Sroberto * Send linkstate to upstream node.
63054359Sroberto */
63154359Srobertostatic void
63254359Srobertocisco_notify(sc_p sc, uint32_t cmd)
63354359Sroberto{
63454359Sroberto	struct ng_mesg *msg;
63554359Sroberto	int dummy_error = 0;
63654359Sroberto
63754359Sroberto	if (sc->inet.hook == NULL) /* nothing to notify */
63854359Sroberto		return;
63954359Sroberto
64054359Sroberto	NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT);
64154359Sroberto	if (msg != NULL)
64254359Sroberto		NG_SEND_MSG_HOOK(dummy_error, sc->node, msg, sc->inet.hook, 0);
64354359Sroberto}
64454359Sroberto