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