1 2 /* 3 * ng_rfc1490.c 4 * 5 * Copyright (c) 1996-1999 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Author: Julian Elischer <julian@freebsd.org> 38 * 39 * $FreeBSD$ 40 * $Whistle: ng_rfc1490.c,v 1.22 1999/11/01 09:24:52 julian Exp $ 41 */ 42 43 /* 44 * This node does RFC 1490 multiplexing. 45 * 46 * NOTE: RFC 1490 is updated by RFC 2427. 47 */ 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/errno.h> 52 #include <sys/kernel.h> 53 #include <sys/malloc.h> 54 #include <sys/mbuf.h> 55 #include <sys/errno.h> 56 #include <sys/socket.h> 57 58 #include <net/if.h> 59 #include <netinet/in.h> 60 #include <netinet/if_ether.h> 61 62 #include <netgraph/ng_message.h> 63 #include <netgraph/netgraph.h> 64 #include <netgraph/ng_parse.h> 65 #include <netgraph/ng_rfc1490.h> 66 67 /* 68 * DEFINITIONS 69 */ 70 71 /* Q.922 stuff -- see RFC 1490 */ 72 #define HDLC_UI 0x03 73 74 #define NLPID_IP 0xCC 75 #define NLPID_PPP 0xCF 76 #define NLPID_SNAP 0x80 77 #define NLPID_Q933 0x08 78 #define NLPID_CLNP 0x81 79 #define NLPID_ESIS 0x82 80 #define NLPID_ISIS 0x83 81 82 #define ERROUT(x) do { error = (x); goto done; } while (0) 83 84 /* Encapsulation methods we understand */ 85 enum { 86 NG_RFC1490_ENCAP_IETF_IP = 1, /* see RFC2427, chapter 7, table 1 */ 87 NG_RFC1490_ENCAP_IETF_SNAP, /* see RFC2427, chapter 7, table 2 */ 88 NG_RFC1490_ENCAP_CISCO, /* Cisco's proprietary encapsulation */ 89 }; 90 91 struct ng_rfc1490_encap_t { 92 u_int8_t method; 93 const char *name; 94 }; 95 96 static const struct ng_rfc1490_encap_t ng_rfc1490_encaps[] = { 97 { NG_RFC1490_ENCAP_IETF_IP, "ietf-ip" }, 98 { NG_RFC1490_ENCAP_IETF_SNAP, "ietf-snap" }, 99 { NG_RFC1490_ENCAP_CISCO, "cisco" }, 100 { 0, NULL}, 101 }; 102 103 /* Node private data */ 104 struct ng_rfc1490_private { 105 hook_p downlink; 106 hook_p ppp; 107 hook_p inet; 108 hook_p ethernet; 109 const struct ng_rfc1490_encap_t *enc; 110 }; 111 typedef struct ng_rfc1490_private *priv_p; 112 113 /* Netgraph node methods */ 114 static ng_constructor_t ng_rfc1490_constructor; 115 static ng_rcvmsg_t ng_rfc1490_rcvmsg; 116 static ng_shutdown_t ng_rfc1490_shutdown; 117 static ng_newhook_t ng_rfc1490_newhook; 118 static ng_rcvdata_t ng_rfc1490_rcvdata; 119 static ng_disconnect_t ng_rfc1490_disconnect; 120 121 /* List of commands and how to convert arguments to/from ASCII */ 122 static const struct ng_cmdlist ng_rfc1490_cmds[] = { 123 { 124 NGM_RFC1490_COOKIE, 125 NGM_RFC1490_SET_ENCAP, 126 "setencap", 127 &ng_parse_string_type, 128 NULL 129 }, 130 { 131 NGM_RFC1490_COOKIE, 132 NGM_RFC1490_GET_ENCAP, 133 "getencap", 134 NULL, 135 &ng_parse_string_type 136 }, 137 { 0 } 138 }; 139 140 /* Node type descriptor */ 141 static struct ng_type typestruct = { 142 .version = NG_ABI_VERSION, 143 .name = NG_RFC1490_NODE_TYPE, 144 .constructor = ng_rfc1490_constructor, 145 .rcvmsg = ng_rfc1490_rcvmsg, 146 .shutdown = ng_rfc1490_shutdown, 147 .newhook = ng_rfc1490_newhook, 148 .rcvdata = ng_rfc1490_rcvdata, 149 .disconnect = ng_rfc1490_disconnect, 150 .cmdlist = ng_rfc1490_cmds, 151 }; 152 NETGRAPH_INIT(rfc1490, &typestruct); 153 154 /************************************************************************ 155 NETGRAPH NODE STUFF 156 ************************************************************************/ 157 158 /* 159 * Node constructor 160 */ 161 static int 162 ng_rfc1490_constructor(node_p node) 163 { 164 priv_p priv; 165 166 /* Allocate private structure */ 167 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 168 if (priv == NULL) 169 return (ENOMEM); 170 171 /* Initialize to default encapsulation method - ietf-ip */ 172 priv->enc = ng_rfc1490_encaps; 173 174 NG_NODE_SET_PRIVATE(node, priv); 175 176 /* Done */ 177 return (0); 178 } 179 180 /* 181 * Give our ok for a hook to be added 182 */ 183 static int 184 ng_rfc1490_newhook(node_p node, hook_p hook, const char *name) 185 { 186 const priv_p priv = NG_NODE_PRIVATE(node); 187 188 if (!strcmp(name, NG_RFC1490_HOOK_DOWNSTREAM)) { 189 if (priv->downlink) 190 return (EISCONN); 191 priv->downlink = hook; 192 } else if (!strcmp(name, NG_RFC1490_HOOK_PPP)) { 193 if (priv->ppp) 194 return (EISCONN); 195 priv->ppp = hook; 196 } else if (!strcmp(name, NG_RFC1490_HOOK_INET)) { 197 if (priv->inet) 198 return (EISCONN); 199 priv->inet = hook; 200 } else if (!strcmp(name, NG_RFC1490_HOOK_ETHERNET)) { 201 if (priv->ethernet) 202 return (EISCONN); 203 priv->ethernet = hook; 204 } else 205 return (EINVAL); 206 return (0); 207 } 208 209 /* 210 * Receive a control message. 211 */ 212 static int 213 ng_rfc1490_rcvmsg(node_p node, item_p item, hook_p lasthook) 214 { 215 const priv_p priv = NG_NODE_PRIVATE(node); 216 struct ng_mesg *msg; 217 struct ng_mesg *resp = NULL; 218 int error = 0; 219 220 NGI_GET_MSG(item, msg); 221 222 if (msg->header.typecookie == NGM_RFC1490_COOKIE) { 223 switch (msg->header.cmd) { 224 case NGM_RFC1490_SET_ENCAP: 225 { 226 const struct ng_rfc1490_encap_t *enc; 227 char *s; 228 size_t len; 229 230 if (msg->header.arglen == 0) 231 ERROUT(EINVAL); 232 233 s = (char *)msg->data; 234 len = msg->header.arglen - 1; 235 236 /* Search for matching encapsulation method */ 237 for (enc = ng_rfc1490_encaps; enc->method != 0; enc++ ) 238 if ((strlen(enc->name) == len) && 239 !strncmp(enc->name, s, len)) 240 break; /* found */ 241 242 if (enc->method != 0) 243 priv->enc = enc; 244 else 245 error = EINVAL; 246 break; 247 } 248 case NGM_RFC1490_GET_ENCAP: 249 250 NG_MKRESPONSE(resp, msg, strlen(priv->enc->name) + 1, M_NOWAIT); 251 if (resp == NULL) 252 ERROUT(ENOMEM); 253 254 strlcpy((char *)resp->data, priv->enc->name, 255 strlen(priv->enc->name) + 1); 256 break; 257 258 default: 259 error = EINVAL; 260 break; 261 } 262 } else 263 error = EINVAL; 264 265 done: 266 NG_RESPOND_MSG(error, node, item, resp); 267 NG_FREE_MSG(msg); 268 return (error); 269 } 270 271 /* 272 * Receive data on a hook and encapsulate according to RFC 1490. 273 * Only those nodes marked (*) are supported by this routine so far. 274 * 275 * Q.922 control 276 * | 277 * | 278 * --------------------------------------------------------------------- 279 * | 0x03 | | 280 * UI I Frame Cisco 281 * | | Encapsulation 282 * --------------------------------- -------------- | 283 * | 0x08 | 0x81 |0xCC |0xCF | 0x00 |..01.... |..10.... -------------- 284 * | | | | | 0x80 | | |0x800 | 285 * Q.933 CLNP IP(*) PPP(*) SNAP ISO 8208 ISO 8208 | | 286 * | (rfc1973) | Modulo 8 Modulo 128 IP(*) Others 287 * | | 288 * -------------------- OUI 289 * | | | 290 * L2 ID L3 ID ------------------------- 291 * | User |00-80-C2 |00-00-00 292 * | specified | | 293 * | 0x70 PID Ethertype 294 * | | | 295 * ------------------- -----------------... ---------- 296 * |0x51 |0x4E | |0x4C |0x7 |0xB | |0x806 | 297 * | | | | | | | | | 298 * 7776 Q.922 Others 802.2 802.3(*) 802.6 Others IP(*) Others 299 * 300 * 301 */ 302 303 #define MAX_ENCAPS_HDR 8 304 #define OUICMP(P,A,B,C) ((P)[0]==(A) && (P)[1]==(B) && (P)[2]==(C)) 305 306 static int 307 ng_rfc1490_rcvdata(hook_p hook, item_p item) 308 { 309 const node_p node = NG_HOOK_NODE(hook); 310 const priv_p priv = NG_NODE_PRIVATE(node); 311 int error = 0; 312 struct mbuf *m; 313 314 NGI_GET_M(item, m); 315 if (hook == priv->downlink) { 316 const u_char *start; 317 const u_char *ptr; 318 319 if (m->m_len < MAX_ENCAPS_HDR 320 && !(m = m_pullup(m, MAX_ENCAPS_HDR))) 321 ERROUT(ENOBUFS); 322 ptr = start = mtod(m, const u_char *); 323 324 if (priv->enc->method == NG_RFC1490_ENCAP_CISCO) 325 goto switch_on_etype; 326 327 /* Must be UI frame */ 328 if (*ptr++ != HDLC_UI) 329 ERROUT(0); 330 331 /* Eat optional zero pad byte */ 332 if (*ptr == 0x00) 333 ptr++; 334 335 /* Multiplex on NLPID */ 336 switch (*ptr++) { 337 case NLPID_SNAP: 338 if (OUICMP(ptr, 0, 0, 0)) { /* It's an ethertype */ 339 u_int16_t etype; 340 341 ptr += 3; 342 switch_on_etype: etype = ntohs(*((const u_int16_t *)ptr)); 343 ptr += 2; 344 m_adj(m, ptr - start); 345 switch (etype) { 346 case ETHERTYPE_IP: 347 NG_FWD_NEW_DATA(error, item, 348 priv->inet, m); 349 break; 350 case ETHERTYPE_ARP: 351 case ETHERTYPE_REVARP: 352 default: 353 ERROUT(0); 354 } 355 } else if (OUICMP(ptr, 0x00, 0x80, 0xc2)) { 356 /* 802.1 bridging */ 357 ptr += 3; 358 if (*ptr++ != 0x00) 359 ERROUT(0); /* unknown PID octet 0 */ 360 if (*ptr++ != 0x07) 361 ERROUT(0); /* not FCS-less 802.3 */ 362 m_adj(m, ptr - start); 363 NG_FWD_NEW_DATA(error, item, priv->ethernet, m); 364 } else /* Other weird stuff... */ 365 ERROUT(0); 366 break; 367 case NLPID_IP: 368 m_adj(m, ptr - start); 369 NG_FWD_NEW_DATA(error, item, priv->inet, m); 370 break; 371 case NLPID_PPP: 372 m_adj(m, ptr - start); 373 NG_FWD_NEW_DATA(error, item, priv->ppp, m); 374 break; 375 case NLPID_Q933: 376 case NLPID_CLNP: 377 case NLPID_ESIS: 378 case NLPID_ISIS: 379 ERROUT(0); 380 default: /* Try PPP (see RFC 1973) */ 381 ptr--; /* NLPID becomes PPP proto */ 382 if ((*ptr & 0x01) == 0x01) 383 ERROUT(0); 384 m_adj(m, ptr - start); 385 NG_FWD_NEW_DATA(error, item, priv->ppp, m); 386 break; 387 } 388 } else if (hook == priv->ppp) { 389 M_PREPEND(m, 2, M_DONTWAIT); /* Prepend PPP NLPID */ 390 if (!m) 391 ERROUT(ENOBUFS); 392 mtod(m, u_char *)[0] = HDLC_UI; 393 mtod(m, u_char *)[1] = NLPID_PPP; 394 NG_FWD_NEW_DATA(error, item, priv->downlink, m); 395 } else if (hook == priv->inet) { 396 switch (priv->enc->method) { 397 case NG_RFC1490_ENCAP_IETF_IP: 398 M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP NLPID */ 399 if (!m) 400 ERROUT(ENOBUFS); 401 mtod(m, u_char *)[0] = HDLC_UI; 402 mtod(m, u_char *)[1] = NLPID_IP; 403 break; 404 case NG_RFC1490_ENCAP_IETF_SNAP: 405 /* 406 * According to RFC2427 frame should begin with 407 * HDLC_UI PAD NLIPID OUI PID 408 * 03 00 80 00 00 00 08 00 409 */ 410 M_PREPEND(m, 8, M_DONTWAIT); 411 if (!m) 412 ERROUT(ENOBUFS); 413 mtod(m, u_char *)[0] = HDLC_UI; 414 mtod(m, u_char *)[1] = 0x00; /* PAD */ 415 mtod(m, u_char *)[2] = NLPID_SNAP; 416 bzero((char *)(mtod(m, u_char *) + 3), 3); /* OUI 0-0-0 */ 417 *((u_int16_t *)mtod(m, u_int16_t *) + 6/sizeof(u_int16_t)) 418 = htons(ETHERTYPE_IP); /* PID */ 419 break; 420 case NG_RFC1490_ENCAP_CISCO: 421 M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP ethertype */ 422 if (!m) 423 ERROUT(ENOBUFS); 424 *((u_int16_t *)mtod(m, u_int16_t *)) = htons(ETHERTYPE_IP); 425 break; 426 } 427 NG_FWD_NEW_DATA(error, item, priv->downlink, m); 428 } else if (hook == priv->ethernet) { 429 M_PREPEND(m, 8, M_DONTWAIT); /* Prepend NLPID, OUI, PID */ 430 if (!m) 431 ERROUT(ENOBUFS); 432 mtod(m, u_char *)[0] = HDLC_UI; 433 mtod(m, u_char *)[1] = 0x00; /* pad */ 434 mtod(m, u_char *)[2] = NLPID_SNAP; 435 mtod(m, u_char *)[3] = 0x00; /* OUI */ 436 mtod(m, u_char *)[4] = 0x80; 437 mtod(m, u_char *)[5] = 0xc2; 438 mtod(m, u_char *)[6] = 0x00; /* PID */ 439 mtod(m, u_char *)[7] = 0x07; 440 NG_FWD_NEW_DATA(error, item, priv->downlink, m); 441 } else 442 panic(__func__); 443 444 done: 445 if (item) 446 NG_FREE_ITEM(item); 447 NG_FREE_M(m); 448 return (error); 449 } 450 451 /* 452 * Nuke node 453 */ 454 static int 455 ng_rfc1490_shutdown(node_p node) 456 { 457 const priv_p priv = NG_NODE_PRIVATE(node); 458 459 /* Take down netgraph node */ 460 bzero(priv, sizeof(*priv)); 461 FREE(priv, M_NETGRAPH); 462 NG_NODE_SET_PRIVATE(node, NULL); 463 NG_NODE_UNREF(node); /* let the node escape */ 464 return (0); 465 } 466 467 /* 468 * Hook disconnection 469 */ 470 static int 471 ng_rfc1490_disconnect(hook_p hook) 472 { 473 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 474 475 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 476 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 477 ng_rmnode_self(NG_HOOK_NODE(hook)); 478 else if (hook == priv->downlink) 479 priv->downlink = NULL; 480 else if (hook == priv->inet) 481 priv->inet = NULL; 482 else if (hook == priv->ppp) 483 priv->ppp = NULL; 484 else if (hook == priv->ethernet) 485 priv->ethernet = NULL; 486 else 487 panic(__func__); 488 return (0); 489 } 490 491