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_NOWAIT | M_ZERO); 169 if (priv == NULL) 170 return (ENOMEM); 171 172 /* Initialize to default encapsulation method - ietf-ip */ 173 priv->enc = ng_rfc1490_encaps; 174 175 NG_NODE_SET_PRIVATE(node, priv); 176 177 /* Done */ 178 return (0); 179 } 180 181 /* 182 * Give our ok for a hook to be added 183 */ 184 static int 185 ng_rfc1490_newhook(node_p node, hook_p hook, const char *name) 186 { 187 const priv_p priv = NG_NODE_PRIVATE(node); 188 189 if (!strcmp(name, NG_RFC1490_HOOK_DOWNSTREAM)) { 190 if (priv->downlink) 191 return (EISCONN); 192 priv->downlink = hook; 193 } else if (!strcmp(name, NG_RFC1490_HOOK_PPP)) { 194 if (priv->ppp) 195 return (EISCONN); 196 priv->ppp = hook; 197 } else if (!strcmp(name, NG_RFC1490_HOOK_INET)) { 198 if (priv->inet) 199 return (EISCONN); 200 priv->inet = hook; 201 } else if (!strcmp(name, NG_RFC1490_HOOK_ETHERNET)) { 202 if (priv->ethernet) 203 return (EISCONN); 204 priv->ethernet = hook; 205 } else 206 return (EINVAL); 207 return (0); 208 } 209 210 /* 211 * Receive a control message. 212 */ 213 static int 214 ng_rfc1490_rcvmsg(node_p node, item_p item, hook_p lasthook) 215 { 216 const priv_p priv = NG_NODE_PRIVATE(node); 217 struct ng_mesg *msg; 218 struct ng_mesg *resp = NULL; 219 int error = 0; 220 221 NGI_GET_MSG(item, msg); 222 223 if (msg->header.typecookie == NGM_RFC1490_COOKIE) { 224 switch (msg->header.cmd) { 225 case NGM_RFC1490_SET_ENCAP: 226 { 227 const struct ng_rfc1490_encap_t *enc; 228 char *s; 229 size_t len; 230 231 if (msg->header.arglen == 0) 232 ERROUT(EINVAL); 233 234 s = (char *)msg->data; 235 len = msg->header.arglen - 1; 236 237 /* Search for matching encapsulation method */ 238 for (enc = ng_rfc1490_encaps; enc->method != 0; enc++ ) 239 if ((strlen(enc->name) == len) && 240 !strncmp(enc->name, s, len)) 241 break; /* found */ 242 243 if (enc->method != 0) 244 priv->enc = enc; 245 else 246 error = EINVAL; 247 break; 248 } 249 case NGM_RFC1490_GET_ENCAP: 250 251 NG_MKRESPONSE(resp, msg, strlen(priv->enc->name) + 1, M_NOWAIT); 252 if (resp == NULL) 253 ERROUT(ENOMEM); 254 255 strlcpy((char *)resp->data, priv->enc->name, 256 strlen(priv->enc->name) + 1); 257 break; 258 259 default: 260 error = EINVAL; 261 break; 262 } 263 } else 264 error = EINVAL; 265 266 done: 267 NG_RESPOND_MSG(error, node, item, resp); 268 NG_FREE_MSG(msg); 269 return (error); 270 } 271 272 /* 273 * Receive data on a hook and encapsulate according to RFC 1490. 274 * Only those nodes marked (*) are supported by this routine so far. 275 * 276 * Q.922 control 277 * | 278 * | 279 * --------------------------------------------------------------------- 280 * | 0x03 | | 281 * UI I Frame Cisco 282 * | | Encapsulation 283 * --------------------------------- -------------- | 284 * | 0x08 | 0x81 |0xCC |0xCF | 0x00 |..01.... |..10.... -------------- 285 * | | | | | 0x80 | | |0x800 | 286 * Q.933 CLNP IP(*) PPP(*) SNAP ISO 8208 ISO 8208 | | 287 * | (rfc1973) | Modulo 8 Modulo 128 IP(*) Others 288 * | | 289 * -------------------- OUI 290 * | | | 291 * L2 ID L3 ID ------------------------- 292 * | User |00-80-C2 |00-00-00 293 * | specified | | 294 * | 0x70 PID Ethertype 295 * | | | 296 * ------------------- -----------------... ---------- 297 * |0x51 |0x4E | |0x4C |0x7 |0xB | |0x806 | 298 * | | | | | | | | | 299 * 7776 Q.922 Others 802.2 802.3(*) 802.6 Others IP(*) Others 300 * 301 * 302 */ 303 304 #define MAX_ENCAPS_HDR 8 305 #define OUICMP(P,A,B,C) ((P)[0]==(A) && (P)[1]==(B) && (P)[2]==(C)) 306 307 static int 308 ng_rfc1490_rcvdata(hook_p hook, item_p item) 309 { 310 const node_p node = NG_HOOK_NODE(hook); 311 const priv_p priv = NG_NODE_PRIVATE(node); 312 int error = 0; 313 struct mbuf *m; 314 315 NGI_GET_M(item, m); 316 if (hook == priv->downlink) { 317 const u_char *start; 318 const u_char *ptr; 319 320 if (m->m_len < MAX_ENCAPS_HDR 321 && !(m = m_pullup(m, MAX_ENCAPS_HDR))) 322 ERROUT(ENOBUFS); 323 ptr = start = mtod(m, const u_char *); 324 325 if (priv->enc->method == NG_RFC1490_ENCAP_CISCO) 326 goto switch_on_etype; 327 328 /* Must be UI frame */ 329 if (*ptr++ != HDLC_UI) 330 ERROUT(0); 331 332 /* Eat optional zero pad byte */ 333 if (*ptr == 0x00) 334 ptr++; 335 336 /* Multiplex on NLPID */ 337 switch (*ptr++) { 338 case NLPID_SNAP: 339 if (OUICMP(ptr, 0, 0, 0)) { /* It's an ethertype */ 340 u_int16_t etype; 341 342 ptr += 3; 343 switch_on_etype: etype = ntohs(*((const u_int16_t *)ptr)); 344 ptr += 2; 345 m_adj(m, ptr - start); 346 switch (etype) { 347 case ETHERTYPE_IP: 348 NG_FWD_NEW_DATA(error, item, 349 priv->inet, m); 350 break; 351 case ETHERTYPE_ARP: 352 case ETHERTYPE_REVARP: 353 default: 354 ERROUT(0); 355 } 356 } else if (OUICMP(ptr, 0x00, 0x80, 0xc2)) { 357 /* 802.1 bridging */ 358 ptr += 3; 359 if (*ptr++ != 0x00) 360 ERROUT(0); /* unknown PID octet 0 */ 361 if (*ptr++ != 0x07) 362 ERROUT(0); /* not FCS-less 802.3 */ 363 m_adj(m, ptr - start); 364 NG_FWD_NEW_DATA(error, item, priv->ethernet, m); 365 } else /* Other weird stuff... */ 366 ERROUT(0); 367 break; 368 case NLPID_IP: 369 m_adj(m, ptr - start); 370 NG_FWD_NEW_DATA(error, item, priv->inet, m); 371 break; 372 case NLPID_PPP: 373 m_adj(m, ptr - start); 374 NG_FWD_NEW_DATA(error, item, priv->ppp, m); 375 break; 376 case NLPID_Q933: 377 case NLPID_CLNP: 378 case NLPID_ESIS: 379 case NLPID_ISIS: 380 ERROUT(0); 381 default: /* Try PPP (see RFC 1973) */ 382 ptr--; /* NLPID becomes PPP proto */ 383 if ((*ptr & 0x01) == 0x01) 384 ERROUT(0); 385 m_adj(m, ptr - start); 386 NG_FWD_NEW_DATA(error, item, priv->ppp, m); 387 break; 388 } 389 } else if (hook == priv->ppp) { 390 M_PREPEND(m, 2, M_DONTWAIT); /* Prepend PPP NLPID */ 391 if (!m) 392 ERROUT(ENOBUFS); 393 mtod(m, u_char *)[0] = HDLC_UI; 394 mtod(m, u_char *)[1] = NLPID_PPP; 395 NG_FWD_NEW_DATA(error, item, priv->downlink, m); 396 } else if (hook == priv->inet) { 397 switch (priv->enc->method) { 398 case NG_RFC1490_ENCAP_IETF_IP: 399 M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP NLPID */ 400 if (!m) 401 ERROUT(ENOBUFS); 402 mtod(m, u_char *)[0] = HDLC_UI; 403 mtod(m, u_char *)[1] = NLPID_IP; 404 break; 405 case NG_RFC1490_ENCAP_IETF_SNAP: 406 /* 407 * According to RFC2427 frame should begin with 408 * HDLC_UI PAD NLIPID OUI PID 409 * 03 00 80 00 00 00 08 00 410 */ 411 M_PREPEND(m, 8, M_DONTWAIT); 412 if (!m) 413 ERROUT(ENOBUFS); 414 mtod(m, u_char *)[0] = HDLC_UI; 415 mtod(m, u_char *)[1] = 0x00; /* PAD */ 416 mtod(m, u_char *)[2] = NLPID_SNAP; 417 bzero((char *)(mtod(m, u_char *) + 3), 3); /* OUI 0-0-0 */ 418 *((u_int16_t *)mtod(m, u_int16_t *) + 6/sizeof(u_int16_t)) 419 = htons(ETHERTYPE_IP); /* PID */ 420 break; 421 case NG_RFC1490_ENCAP_CISCO: 422 M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP ethertype */ 423 if (!m) 424 ERROUT(ENOBUFS); 425 *((u_int16_t *)mtod(m, u_int16_t *)) = htons(ETHERTYPE_IP); 426 break; 427 } 428 NG_FWD_NEW_DATA(error, item, priv->downlink, m); 429 } else if (hook == priv->ethernet) { 430 M_PREPEND(m, 8, M_DONTWAIT); /* Prepend NLPID, OUI, PID */ 431 if (!m) 432 ERROUT(ENOBUFS); 433 mtod(m, u_char *)[0] = HDLC_UI; 434 mtod(m, u_char *)[1] = 0x00; /* pad */ 435 mtod(m, u_char *)[2] = NLPID_SNAP; 436 mtod(m, u_char *)[3] = 0x00; /* OUI */ 437 mtod(m, u_char *)[4] = 0x80; 438 mtod(m, u_char *)[5] = 0xc2; 439 mtod(m, u_char *)[6] = 0x00; /* PID */ 440 mtod(m, u_char *)[7] = 0x07; 441 NG_FWD_NEW_DATA(error, item, priv->downlink, m); 442 } else 443 panic("%s", __func__); 444 445 done: 446 if (item) 447 NG_FREE_ITEM(item); 448 NG_FREE_M(m); 449 return (error); 450 } 451 452 /* 453 * Nuke node 454 */ 455 static int 456 ng_rfc1490_shutdown(node_p node) 457 { 458 const priv_p priv = NG_NODE_PRIVATE(node); 459 460 /* Take down netgraph node */ 461 bzero(priv, sizeof(*priv)); 462 free(priv, M_NETGRAPH); 463 NG_NODE_SET_PRIVATE(node, NULL); 464 NG_NODE_UNREF(node); /* let the node escape */ 465 return (0); 466 } 467 468 /* 469 * Hook disconnection 470 */ 471 static int 472 ng_rfc1490_disconnect(hook_p hook) 473 { 474 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 475 476 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 477 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 478 ng_rmnode_self(NG_HOOK_NODE(hook)); 479 else if (hook == priv->downlink) 480 priv->downlink = NULL; 481 else if (hook == priv->inet) 482 priv->inet = NULL; 483 else if (hook == priv->ppp) 484 priv->ppp = NULL; 485 else if (hook == priv->ethernet) 486 priv->ethernet = NULL; 487 else 488 panic("%s", __func__); 489 return (0); 490 } 491 492