1/* 2 * ng_sppp.c Netgraph to Sppp module. 3 */ 4 5/*- 6 * Copyright (C) 2002-2004 Cronyx Engineering. 7 * Copyright (C) 2002-2004 Roman Kurakin <rik@cronyx.ru> 8 * 9 * This software is distributed with NO WARRANTIES, not even the implied 10 * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 * 12 * Authors grant any other persons or organisations a permission to use, 13 * modify and redistribute this software in source and binary forms, 14 * as long as this message is kept with the software, all derivative 15 * works or modified versions. 16 * 17 * Cronyx Id: ng_sppp.c,v 1.1.2.10 2004/03/01 15:17:21 rik Exp $ 18 */ 19#include <sys/cdefs.h> 20__FBSDID("$FreeBSD$"); 21 22#include <sys/param.h> 23#include <sys/systm.h> 24#include <sys/errno.h> 25#include <sys/kernel.h> 26#include <sys/malloc.h> 27#include <sys/mbuf.h> 28#include <sys/errno.h> 29#include <sys/sockio.h> 30#include <sys/socket.h> 31#include <sys/syslog.h> 32#include <sys/libkern.h> 33 34#include <net/if.h> 35#include <net/if_var.h> 36#include <net/if_types.h> 37#include <net/bpf.h> 38#include <net/if_sppp.h> 39 40#include <netinet/in.h> 41 42#include <netgraph/ng_message.h> 43#include <netgraph/netgraph.h> 44#include <netgraph/ng_parse.h> 45#include <netgraph/ng_sppp.h> 46 47#ifdef NG_SEPARATE_MALLOC 48static MALLOC_DEFINE(M_NETGRAPH_SPPP, "netgraph_sppp", "netgraph sppp node"); 49#else 50#define M_NETGRAPH_SPPP M_NETGRAPH 51#endif 52 53/* Node private data */ 54struct ng_sppp_private { 55 struct ifnet *ifp; /* Our interface */ 56 int unit; /* Interface unit number */ 57 node_p node; /* Our netgraph node */ 58 hook_p hook; /* Hook */ 59}; 60typedef struct ng_sppp_private *priv_p; 61 62/* Interface methods */ 63static void ng_sppp_start (struct ifnet *ifp); 64static int ng_sppp_ioctl (struct ifnet *ifp, u_long cmd, caddr_t data); 65 66/* Netgraph methods */ 67static ng_constructor_t ng_sppp_constructor; 68static ng_rcvmsg_t ng_sppp_rcvmsg; 69static ng_shutdown_t ng_sppp_shutdown; 70static ng_newhook_t ng_sppp_newhook; 71static ng_rcvdata_t ng_sppp_rcvdata; 72static ng_disconnect_t ng_sppp_disconnect; 73 74/* List of commands and how to convert arguments to/from ASCII */ 75static const struct ng_cmdlist ng_sppp_cmds[] = { 76 { 77 NGM_SPPP_COOKIE, 78 NGM_SPPP_GET_IFNAME, 79 "getifname", 80 NULL, 81 &ng_parse_string_type 82 }, 83 { 0 } 84}; 85 86/* Node type descriptor */ 87static struct ng_type typestruct = { 88 .version = NG_ABI_VERSION, 89 .name = NG_SPPP_NODE_TYPE, 90 .constructor = ng_sppp_constructor, 91 .rcvmsg = ng_sppp_rcvmsg, 92 .shutdown = ng_sppp_shutdown, 93 .newhook = ng_sppp_newhook, 94 .rcvdata = ng_sppp_rcvdata, 95 .disconnect = ng_sppp_disconnect, 96 .cmdlist = ng_sppp_cmds, 97}; 98NETGRAPH_INIT(sppp, &typestruct); 99 100MODULE_DEPEND (ng_sppp, sppp, 1, 1, 1); 101 102/* We keep a bitmap indicating which unit numbers are free. 103 Zero means the unit number is free, one means it's taken. */ 104static unsigned char *ng_sppp_units = NULL; 105static unsigned char ng_sppp_units_len = 0; 106static unsigned char ng_units_in_use = 0; 107 108/* 109 * Find the first free unit number for a new interface. 110 * Increase the size of the unit bitmap as necessary. 111 */ 112static __inline void 113ng_sppp_get_unit (int *unit) 114{ 115 int index, bit; 116 unsigned char mask; 117 118 for (index = 0; index < ng_sppp_units_len 119 && ng_sppp_units[index] == 0xFF; index++); 120 if (index == ng_sppp_units_len) { /* extend array */ 121 unsigned char *newarray; 122 int newlen; 123 124 newlen = (2 * ng_sppp_units_len) + sizeof (*ng_sppp_units); 125 newarray = malloc (newlen * sizeof (*ng_sppp_units), 126 M_NETGRAPH_SPPP, M_WAITOK); 127 bcopy (ng_sppp_units, newarray, 128 ng_sppp_units_len * sizeof (*ng_sppp_units)); 129 bzero (newarray + ng_sppp_units_len, 130 newlen - ng_sppp_units_len); 131 if (ng_sppp_units != NULL) 132 free (ng_sppp_units, M_NETGRAPH_SPPP); 133 ng_sppp_units = newarray; 134 ng_sppp_units_len = newlen; 135 } 136 mask = ng_sppp_units[index]; 137 for (bit = 0; (mask & 1) != 0; bit++) 138 mask >>= 1; 139 KASSERT ((bit >= 0 && bit < NBBY), 140 ("%s: word=%d bit=%d", __func__, ng_sppp_units[index], bit)); 141 ng_sppp_units[index] |= (1 << bit); 142 *unit = (index * NBBY) + bit; 143 ng_units_in_use++; 144} 145 146/* 147 * Free a no longer needed unit number. 148 */ 149static __inline void 150ng_sppp_free_unit (int unit) 151{ 152 int index, bit; 153 154 index = unit / NBBY; 155 bit = unit % NBBY; 156 KASSERT (index < ng_sppp_units_len, 157 ("%s: unit=%d len=%d", __func__, unit, ng_sppp_units_len)); 158 KASSERT ((ng_sppp_units[index] & (1 << bit)) != 0, 159 ("%s: unit=%d is free", __func__, unit)); 160 ng_sppp_units[index] &= ~(1 << bit); 161 162 ng_units_in_use--; 163 if (ng_units_in_use == 0) { 164 free (ng_sppp_units, M_NETGRAPH_SPPP); 165 ng_sppp_units_len = 0; 166 ng_sppp_units = NULL; 167 } 168} 169 170/************************************************************************ 171 INTERFACE STUFF 172 ************************************************************************/ 173 174/* 175 * Process an ioctl for the interface 176 */ 177static int 178ng_sppp_ioctl (struct ifnet *ifp, u_long command, caddr_t data) 179{ 180 int error = 0; 181 182 error = sppp_ioctl (ifp, command, data); 183 if (error) 184 return error; 185 186 return error; 187} 188 189/* 190 * This routine should never be called 191 */ 192 193static void 194ng_sppp_start (struct ifnet *ifp) 195{ 196 struct mbuf *m; 197 int len, error = 0; 198 priv_p priv = ifp->if_softc; 199 200 /* Check interface flags */ 201 /* 202 * This has side effects. It is not good idea to stop sending if we 203 * are not UP. If we are not running we still want to send LCP term 204 * packets. 205 */ 206/* if (!((ifp->if_flags & IFF_UP) && */ 207/* (ifp->if_drv_flags & IFF_DRV_RUNNING))) { */ 208/* return;*/ 209/* }*/ 210 211 if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 212 return; 213 214 if (!priv->hook) 215 return; 216 217 ifp->if_drv_flags |= IFF_DRV_OACTIVE; 218 219 while ((m = sppp_dequeue (ifp)) != NULL) { 220 BPF_MTAP (ifp, m); 221 len = m->m_pkthdr.len; 222 223 NG_SEND_DATA_ONLY (error, priv->hook, m); 224 225 if (error) { 226 ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 227 return; 228 } 229 } 230 ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 231} 232 233/************************************************************************ 234 NETGRAPH NODE STUFF 235 ************************************************************************/ 236 237/* 238 * Constructor for a node 239 */ 240static int 241ng_sppp_constructor (node_p node) 242{ 243 struct sppp *pp; 244 struct ifnet *ifp; 245 priv_p priv; 246 247 /* Allocate node and interface private structures */ 248 priv = malloc(sizeof(*priv), M_NETGRAPH_SPPP, M_WAITOK | M_ZERO); 249 250 ifp = if_alloc(IFT_PPP); 251 if (ifp == NULL) { 252 free (priv, M_NETGRAPH_SPPP); 253 return (ENOSPC); 254 } 255 pp = IFP2SP(ifp); 256 257 /* Link them together */ 258 ifp->if_softc = priv; 259 priv->ifp = ifp; 260 261 /* Get an interface unit number */ 262 ng_sppp_get_unit(&priv->unit); 263 264 /* Link together node and private info */ 265 NG_NODE_SET_PRIVATE (node, priv); 266 priv->node = node; 267 268 /* Initialize interface structure */ 269 if_initname (SP2IFP(pp), NG_SPPP_IFACE_NAME, priv->unit); 270 ifp->if_start = ng_sppp_start; 271 ifp->if_ioctl = ng_sppp_ioctl; 272 ifp->if_flags = (IFF_POINTOPOINT|IFF_MULTICAST); 273 274 /* Give this node the same name as the interface (if possible) */ 275 if (ng_name_node(node, SP2IFP(pp)->if_xname) != 0) 276 log (LOG_WARNING, "%s: can't acquire netgraph name\n", 277 SP2IFP(pp)->if_xname); 278 279 /* Attach the interface */ 280 sppp_attach (ifp); 281 if_attach (ifp); 282 bpfattach (ifp, DLT_NULL, sizeof(u_int32_t)); 283 284 /* Done */ 285 return (0); 286} 287 288/* 289 * Give our ok for a hook to be added 290 */ 291static int 292ng_sppp_newhook (node_p node, hook_p hook, const char *name) 293{ 294 priv_p priv = NG_NODE_PRIVATE (node); 295 296 if (strcmp (name, NG_SPPP_HOOK_DOWNSTREAM) != 0) 297 return (EINVAL); 298 299 if (priv->hook) 300 return (EISCONN); 301 302 priv->hook = hook; 303 NG_HOOK_SET_PRIVATE (hook, priv); 304 305 return (0); 306} 307 308/* 309 * Receive a control message 310 */ 311static int 312ng_sppp_rcvmsg (node_p node, item_p item, hook_p lasthook) 313{ 314 const priv_p priv = NG_NODE_PRIVATE (node); 315 struct ng_mesg *msg = NULL; 316 struct ng_mesg *resp = NULL; 317 struct sppp *const pp = IFP2SP(priv->ifp); 318 int error = 0; 319 320 NGI_GET_MSG (item, msg); 321 switch (msg->header.typecookie) { 322 case NGM_SPPP_COOKIE: 323 switch (msg->header.cmd) { 324 case NGM_SPPP_GET_IFNAME: 325 NG_MKRESPONSE (resp, msg, IFNAMSIZ, M_NOWAIT); 326 if (!resp) { 327 error = ENOMEM; 328 break; 329 } 330 strlcpy(resp->data, SP2IFP(pp)->if_xname, IFNAMSIZ); 331 break; 332 333 default: 334 error = EINVAL; 335 break; 336 } 337 break; 338 default: 339 error = EINVAL; 340 break; 341 } 342 NG_RESPOND_MSG (error, node, item, resp); 343 NG_FREE_MSG (msg); 344 return (error); 345} 346 347/* 348 * Recive data from a hook. Pass the packet to the correct input routine. 349 */ 350static int 351ng_sppp_rcvdata (hook_p hook, item_p item) 352{ 353 struct mbuf *m; 354 const priv_p priv = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); 355 struct sppp *const pp = IFP2SP(priv->ifp); 356 357 NGI_GET_M (item, m); 358 NG_FREE_ITEM (item); 359 /* Sanity checks */ 360 KASSERT (m->m_flags & M_PKTHDR, ("%s: not pkthdr", __func__)); 361 if ((SP2IFP(pp)->if_flags & IFF_UP) == 0) { 362 NG_FREE_M (m); 363 return (ENETDOWN); 364 } 365 366 /* Update interface stats */ 367 if_inc_counter(SP2IFP(pp), IFCOUNTER_IPACKETS, 1); 368 369 /* Note receiving interface */ 370 m->m_pkthdr.rcvif = SP2IFP(pp); 371 372 /* Berkeley packet filter */ 373 BPF_MTAP (SP2IFP(pp), m); 374 375 /* Send packet */ 376 sppp_input (SP2IFP(pp), m); 377 return 0; 378} 379 380/* 381 * Shutdown and remove the node and its associated interface. 382 */ 383static int 384ng_sppp_shutdown (node_p node) 385{ 386 const priv_p priv = NG_NODE_PRIVATE(node); 387 /* Detach from the packet filter list of interfaces. */ 388 bpfdetach (priv->ifp); 389 sppp_detach (priv->ifp); 390 if_detach (priv->ifp); 391 if_free(priv->ifp); 392 ng_sppp_free_unit (priv->unit); 393 free (priv, M_NETGRAPH_SPPP); 394 NG_NODE_SET_PRIVATE (node, NULL); 395 NG_NODE_UNREF (node); 396 return (0); 397} 398 399/* 400 * Hook disconnection. 401 */ 402static int 403ng_sppp_disconnect (hook_p hook) 404{ 405 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 406 407 if (priv) 408 priv->hook = NULL; 409 410 return (0); 411} 412