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