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