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