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