1 2 /* 3 * ng_ppp.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: Archie Cobbs <archie@whistle.com> 38 * 39 * $FreeBSD$ 40 * $Whistle: ng_ppp.c,v 1.22 1999/01/28 23:54:53 julian Exp $ 41 */ 42 43 /* 44 * This node does PPP protocol multiplexing based on PPP protocol 45 * ID numbers. This node does not add address and control fields, 46 * as that is considered a ``device layer'' issue. 47 */ 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/kernel.h> 52 #include <sys/conf.h> 53 #include <sys/mbuf.h> 54 #include <sys/malloc.h> 55 #include <sys/errno.h> 56 #include <sys/socket.h> 57 #include <sys/syslog.h> 58 59 #include <netgraph/ng_message.h> 60 #include <netgraph/netgraph.h> 61 #include <netgraph/ng_ppp.h> 62 63 /* Protocol stuff */ 64 #define PROT_DOWNLINK 0xffff 65 #define PROT_BYPASS 0x0000 66 67 #define PROT_VALID(p) (((p) & 0x0101) == 0x0001) 68 #define PROT_COMPRESSIBLE(p) (((p) & 0xFF00) == 0x0000) 69 70 /* Extract protocol from hook private pointer */ 71 #define HOOK_PROTO(hook) (*((u_int16_t *) &hook->private)) 72 73 /* Node private data */ 74 struct private { 75 struct ng_ppp_stat stats; 76 u_int protocomp:1; 77 }; 78 typedef struct private *priv_p; 79 80 /* Protocol aliases */ 81 struct protoalias { 82 char *name; 83 u_int16_t proto; 84 }; 85 86 /* Netgraph node methods */ 87 static int ng_ppp_constructor(node_p *nodep); 88 static int ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, 89 const char *retaddr, struct ng_mesg **resp); 90 static int ng_ppp_rmnode(node_p node); 91 static int ng_ppp_newhook(node_p node, hook_p hook, const char *name); 92 static int ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta); 93 static int ng_ppp_disconnect(hook_p hook); 94 95 /* Helper stuff */ 96 static int ng_ppp_decodehookname(const char *name); 97 static hook_p ng_ppp_findhook(node_p node, int proto); 98 99 /* Node type descriptor */ 100 static struct ng_type typestruct = { 101 NG_VERSION, 102 NG_PPP_NODE_TYPE, 103 NULL, 104 ng_ppp_constructor, 105 ng_ppp_rcvmsg, 106 ng_ppp_rmnode, 107 ng_ppp_newhook, 108 NULL, 109 NULL, 110 ng_ppp_rcvdata, 111 ng_ppp_rcvdata, 112 ng_ppp_disconnect 113 }; 114 NETGRAPH_INIT(ppp, &typestruct); 115 116 /* Protocol aliases */ 117 static const struct protoalias gAliases[] = 118 { 119 { NG_PPP_HOOK_DOWNLINK, PROT_DOWNLINK }, 120 { NG_PPP_HOOK_BYPASS, PROT_BYPASS }, 121 { NG_PPP_HOOK_LCP, 0xc021 }, 122 { NG_PPP_HOOK_IPCP, 0x8021 }, 123 { NG_PPP_HOOK_ATCP, 0x8029 }, 124 { NG_PPP_HOOK_CCP, 0x80fd }, 125 { NG_PPP_HOOK_ECP, 0x8053 }, 126 { NG_PPP_HOOK_IP, 0x0021 }, 127 { NG_PPP_HOOK_VJCOMP, 0x002d }, 128 { NG_PPP_HOOK_VJUNCOMP, 0x002f }, 129 { NG_PPP_HOOK_MP, 0x003d }, 130 { NG_PPP_HOOK_COMPD, 0x00fd }, 131 { NG_PPP_HOOK_CRYPTD, 0x0053 }, 132 { NG_PPP_HOOK_PAP, 0xc023 }, 133 { NG_PPP_HOOK_CHAP, 0xc223 }, 134 { NG_PPP_HOOK_LQR, 0xc025 }, 135 { NULL, 0 } 136 }; 137 138 #define ERROUT(x) do { error = (x); goto done; } while (0) 139 140 /************************************************************************ 141 NETGRAPH NODE STUFF 142 ************************************************************************/ 143 144 /* 145 * Node constructor 146 */ 147 static int 148 ng_ppp_constructor(node_p *nodep) 149 { 150 priv_p priv; 151 int error; 152 153 /* Allocate private structure */ 154 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 155 if (priv == NULL) 156 return (ENOMEM); 157 bzero(priv, sizeof(*priv)); 158 159 /* Call generic node constructor */ 160 if ((error = ng_make_node_common(&typestruct, nodep))) { 161 FREE(priv, M_NETGRAPH); 162 return (error); 163 } 164 (*nodep)->private = priv; 165 166 /* Done */ 167 return (0); 168 } 169 170 /* 171 * Give our OK for a hook to be added 172 */ 173 static int 174 ng_ppp_newhook(node_p node, hook_p hook, const char *name) 175 { 176 const priv_p priv = node->private; 177 int proto; 178 179 /* Decode protocol number */ 180 if ((proto = ng_ppp_decodehookname(name)) < 0) 181 return (EINVAL); 182 183 /* See if already connected */ 184 if (ng_ppp_findhook(node, proto) != NULL) 185 return (EISCONN); 186 187 /* Clear stats when downstream hook reconnected */ 188 if (proto == PROT_DOWNLINK) 189 bzero(&priv->stats, sizeof(priv->stats)); 190 191 /* OK */ 192 HOOK_PROTO(hook) = proto; 193 return (0); 194 } 195 196 /* 197 * Receive a control message 198 */ 199 static int 200 ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, 201 const char *raddr, struct ng_mesg **rptr) 202 { 203 const priv_p priv = node->private; 204 struct ng_mesg *resp = NULL; 205 int error = 0; 206 207 switch (msg->header.typecookie) { 208 case NGM_PPP_COOKIE: 209 switch (msg->header.cmd) { 210 case NGM_PPP_SET_PROTOCOMP: 211 if (msg->header.arglen < sizeof(int)) 212 ERROUT(EINVAL); 213 priv->protocomp = !!*((int *) msg->data); 214 break; 215 case NGM_PPP_GET_STATS: 216 NG_MKRESPONSE(resp, msg, sizeof(priv->stats), M_NOWAIT); 217 if (resp == NULL) 218 ERROUT(ENOMEM); 219 *((struct ng_ppp_stat *) resp->data) = priv->stats; 220 break; 221 case NGM_PPP_CLR_STATS: 222 bzero(&priv->stats, sizeof(priv->stats)); 223 break; 224 default: 225 error = EINVAL; 226 break; 227 } 228 break; 229 default: 230 error = EINVAL; 231 break; 232 } 233 if (rptr) 234 *rptr = resp; 235 else if (resp) 236 FREE(resp, M_NETGRAPH); 237 238 done: 239 FREE(msg, M_NETGRAPH); 240 return (error); 241 } 242 243 /* 244 * Receive data on a hook 245 */ 246 static int 247 ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 248 { 249 const node_p node = hook->node; 250 const priv_p priv = node->private; 251 u_int16_t proto = HOOK_PROTO(hook); 252 int error = 0; 253 254 switch (proto) { 255 256 /* Prepend the (possibly compressed) protocol number */ 257 default: 258 { 259 int psize = (priv->protocomp 260 && PROT_COMPRESSIBLE(proto)) ? 1 : 2; 261 262 M_PREPEND(m, psize, M_NOWAIT); 263 if (!m || !(m = m_pullup(m, psize))) 264 ERROUT(ENOBUFS); 265 if (psize == 1) 266 *mtod(m, u_char *) = proto; 267 else 268 *mtod(m, u_short *) = htons(proto); 269 hook = ng_ppp_findhook(node, PROT_DOWNLINK); 270 break; 271 } 272 273 /* Extract the protocol number and direct to the corresponding hook */ 274 case PROT_DOWNLINK: 275 { 276 /* Stats */ 277 priv->stats.recvFrames++; 278 priv->stats.recvOctets += m->m_pkthdr.len; 279 280 /* Extract protocol number */ 281 for (proto = 0; 282 !PROT_VALID(proto); 283 proto = (proto << 8) + *mtod(m, u_char *), m_adj(m, 1)) { 284 if (m == NULL) { 285 priv->stats.badProto++; 286 ERROUT(EINVAL); 287 } 288 if ((m = m_pullup(m, 1)) == NULL) 289 ERROUT(ENOBUFS); 290 } 291 292 /* Find corresponding hook; if none, use the "unhooked" 293 hook and leave the two-byte protocol prepended */ 294 if ((hook = ng_ppp_findhook(node, proto)) == NULL) { 295 priv->stats.unknownProto++; 296 hook = ng_ppp_findhook(node, PROT_BYPASS); 297 M_PREPEND(m, 2, M_NOWAIT); 298 if (m == NULL || (m = m_pullup(m, 2)) == NULL) 299 ERROUT(ENOBUFS); 300 *mtod(m, u_short *) = htons(proto); 301 } 302 break; 303 } 304 305 /* Send raw data from "unhooked" hook as-is; we assume the 306 protocol is already prepended */ 307 case PROT_BYPASS: 308 hook = ng_ppp_findhook(node, PROT_DOWNLINK); 309 break; 310 } 311 312 /* Stats */ 313 if (m != NULL && hook != NULL && HOOK_PROTO(hook) == PROT_DOWNLINK) { 314 priv->stats.xmitFrames++; 315 priv->stats.xmitOctets += m->m_pkthdr.len; 316 } 317 318 /* Forward packet on hook */ 319 NG_SEND_DATA(error, hook, m, meta); 320 return (error); 321 322 done: 323 /* Something went wrong */ 324 NG_FREE_DATA(m, meta); 325 return (error); 326 } 327 328 /* 329 * Destroy node 330 */ 331 static int 332 ng_ppp_rmnode(node_p node) 333 { 334 const priv_p priv = node->private; 335 336 /* Take down netgraph node */ 337 node->flags |= NG_INVALID; 338 ng_cutlinks(node); 339 ng_unname(node); 340 bzero(priv, sizeof(*priv)); 341 FREE(priv, M_NETGRAPH); 342 node->private = NULL; 343 ng_unref(node); /* let the node escape */ 344 return (0); 345 } 346 347 /* 348 * Hook disconnection 349 */ 350 static int 351 ng_ppp_disconnect(hook_p hook) 352 { 353 if (hook->node->numhooks == 0) 354 ng_rmnode(hook->node); 355 return (0); 356 } 357 358 /************************************************************************ 359 HELPER STUFF 360 ************************************************************************/ 361 362 /* 363 * Decode ASCII protocol name 364 */ 365 static int 366 ng_ppp_decodehookname(const char *name) 367 { 368 int k, proto; 369 370 for (k = 0; gAliases[k].name; k++) 371 if (!strcmp(gAliases[k].name, name)) 372 return (gAliases[k].proto); 373 if (strlen(name) != 6 || name[0] != '0' || name[1] != 'x') 374 return (-1); 375 for (proto = k = 2; k < 6; k++) { 376 const u_char ch = name[k] | 0x20; 377 int dig; 378 379 if (ch >= '0' && ch <= '9') 380 dig = ch - '0'; 381 else if (ch >= 'a' && ch <= 'f') 382 dig = ch - 'a' + 10; 383 else 384 return (-1); 385 proto = (proto << 4) + dig; 386 } 387 if (!PROT_VALID(proto)) 388 return(-1); 389 return (proto); 390 } 391 392 /* 393 * Find a hook by protocol number 394 */ 395 static hook_p 396 ng_ppp_findhook(node_p node, int proto) 397 { 398 hook_p hook; 399 400 LIST_FOREACH(hook, &node->hooks, hooks) { 401 if (HOOK_PROTO(hook) == proto) 402 return (hook); 403 } 404 return (NULL); 405 } 406 407