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