Annotation of embedaddon/libpdel/ppp/ppp_node.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include "ppp/ppp_defs.h"
! 42: #include "ppp/ppp_log.h"
! 43: #include "ppp/ppp_node.h"
! 44:
! 45: #include <sys/queue.h>
! 46: #include <netgraph/ng_socket.h>
! 47:
! 48: /*
! 49: * This manages an ng_ppp(4) netgraph node.
! 50: */
! 51:
! 52: #define NODE_MTYPE "ppp_node"
! 53: #define PKTBUFLEN 2000
! 54: #define NODE_HOOK "ppp"
! 55:
! 56: /* One recipient for incoming control messages */
! 57: struct ppp_node_recvmsg {
! 58: u_int32_t cookie;
! 59: u_int32_t cmd;
! 60: ppp_node_recvmsg_t *recvmsg;
! 61: void *arg;
! 62: TAILQ_ENTRY(ppp_node_recvmsg) next;
! 63: };
! 64:
! 65: /* PPP node structure */
! 66: struct ppp_node {
! 67: int csock; /* ng_socket ctrl socket */
! 68: int dsock; /* ng_socket data socket */
! 69: struct pevent_ctx *ev_ctx; /* event context */
! 70: pthread_mutex_t *mutex; /* mutex */
! 71: struct pevent *cevent; /* incoming ctrl msg event */
! 72: struct pevent *devent; /* incoming packet event */
! 73: struct ppp_log *log; /* log */
! 74: ppp_node_recv_t *recv; /* handler for packets */
! 75: void *rarg; /* recv() function arg */
! 76: struct ng_ppp_node_conf conf; /* ng_ppp(4) node config */
! 77: char path[32]; /* netgraph path to node */
! 78: u_char got_conf; /* "conf" is valid */
! 79: u_char connected[NG_PPP_MAX_LINKS];
! 80: TAILQ_HEAD(,ppp_node_recvmsg) rmlist; /* ng_mesg recipient list */
! 81: };
! 82:
! 83: /* Internal functions */
! 84: static pevent_handler_t ppp_node_read_packet;
! 85: static pevent_handler_t ppp_node_read_message;
! 86:
! 87: /* Macro for logging */
! 88: #define LOG(sev, fmt, args...) PPP_LOG(node->log, sev, fmt , ## args)
! 89:
! 90: /***********************************************************************
! 91: PUBLIC FUNCTIONS
! 92: ***********************************************************************/
! 93:
! 94: /*
! 95: * Create a new PPP node from a newly created link.
! 96: *
! 97: * The "log" is consumed.
! 98: */
! 99: struct ppp_node *
! 100: ppp_node_create(struct pevent_ctx *ev_ctx,
! 101: pthread_mutex_t *mutex, struct ppp_log *log)
! 102: {
! 103: union {
! 104: u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
! 105: struct ng_mesg reply;
! 106: } repbuf;
! 107: struct ng_mesg *reply = &repbuf.reply;
! 108: struct nodeinfo ninfo;
! 109: struct ngm_mkpeer mkpeer;
! 110: struct ppp_node *node;
! 111:
! 112: /* Create new node structure */
! 113: if ((node = MALLOC(NODE_MTYPE, sizeof(*node))) == NULL)
! 114: return (NULL);
! 115: memset(node, 0, sizeof(*node));
! 116: node->ev_ctx = ev_ctx;
! 117: node->mutex = mutex;
! 118: node->log = log;
! 119: node->csock = -1;
! 120: node->dsock = -1;
! 121: TAILQ_INIT(&node->rmlist);
! 122:
! 123: /* Create netgraph socket node */
! 124: if (NgMkSockNode(NULL, &node->csock, &node->dsock) == -1) {
! 125: LOG(LOG_ERR, "%s: %m", "creating socket node");
! 126: goto fail;
! 127: }
! 128: (void)fcntl(node->csock, F_SETFD, 1);
! 129: (void)fcntl(node->dsock, F_SETFD, 1);
! 130:
! 131: /* Attach a new ng_ppp(4) node to the socket node via "bypass" hook */
! 132: memset(&mkpeer, 0, sizeof(mkpeer));
! 133: strlcpy(mkpeer.type, NG_PPP_NODE_TYPE, sizeof(mkpeer.type));
! 134: strlcpy(mkpeer.ourhook, NODE_HOOK, sizeof(mkpeer.ourhook));
! 135: strlcpy(mkpeer.peerhook, NG_PPP_HOOK_BYPASS, sizeof(mkpeer.peerhook));
! 136: if (NgSendMsg(node->csock, ".",
! 137: NGM_GENERIC_COOKIE, NGM_MKPEER, &mkpeer, sizeof(mkpeer)) == -1) {
! 138: LOG(LOG_ERR, "%s: %m", "attaching ppp node");
! 139: goto fail;
! 140: }
! 141:
! 142: /* Get node info including 'id' and create absolute path for node */
! 143: if (NgSendMsg(node->csock, NODE_HOOK,
! 144: NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) == -1) {
! 145: LOG(LOG_ERR, "can't get node info: %m");
! 146: goto fail;
! 147: }
! 148: memset(&repbuf, 0, sizeof(repbuf));
! 149: if (NgRecvMsg(node->csock, reply, sizeof(repbuf), NULL) == -1) {
! 150: LOG(LOG_ERR, "can't read node info: %m");
! 151: goto fail;
! 152: }
! 153: memcpy(&ninfo, reply->data, sizeof(ninfo));
! 154: snprintf(node->path, sizeof(node->path), "[%lx]:", (long)ninfo.id);
! 155:
! 156: /* Register event for reading incoming packets from bypass hook */
! 157: if (pevent_register(node->ev_ctx, &node->devent, PEVENT_RECURRING,
! 158: node->mutex, ppp_node_read_packet, node, PEVENT_READ,
! 159: node->dsock) == -1) {
! 160: LOG(LOG_ERR, "%s: %m", "adding read event");
! 161: goto fail;
! 162: }
! 163:
! 164: /* Register event for reading incoming control messages */
! 165: if (pevent_register(node->ev_ctx, &node->cevent, PEVENT_RECURRING,
! 166: node->mutex, ppp_node_read_message, node, PEVENT_READ,
! 167: node->csock) == -1) {
! 168: LOG(LOG_ERR, "%s: %m", "adding read event");
! 169: goto fail;
! 170: }
! 171:
! 172: /* Done */
! 173: return (node);
! 174:
! 175: fail:
! 176: /* Clean up */
! 177: if (node->csock != -1) {
! 178: (void)NgSendMsg(node->csock, NODE_HOOK,
! 179: NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
! 180: (void)close(node->csock);
! 181: (void)close(node->dsock);
! 182: }
! 183: FREE(NODE_MTYPE, node);
! 184: return (NULL);
! 185: }
! 186:
! 187: /*
! 188: * Destroy a node.
! 189: */
! 190: void
! 191: ppp_node_destroy(struct ppp_node **nodep)
! 192: {
! 193: struct ppp_node *const node = *nodep;
! 194: const int esave = errno;
! 195:
! 196: if (node == NULL)
! 197: return;
! 198: *nodep = NULL;
! 199: while (!TAILQ_EMPTY(&node->rmlist)) {
! 200: struct ppp_node_recvmsg *const rm = TAILQ_FIRST(&node->rmlist);
! 201:
! 202: ppp_node_set_recvmsg(node, rm->cookie, rm->cmd, NULL, NULL);
! 203: }
! 204: (void)NgSendMsg(node->csock, NODE_HOOK,
! 205: NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
! 206: (void)close(node->csock);
! 207: (void)close(node->dsock);
! 208: pevent_unregister(&node->devent);
! 209: pevent_unregister(&node->cevent);
! 210: ppp_log_close(&node->log);
! 211: FREE(NODE_MTYPE, node);
! 212: errno = esave;
! 213: }
! 214:
! 215: /*
! 216: * Write to bypass hook.
! 217: */
! 218: int
! 219: ppp_node_write(struct ppp_node *node, u_int link_num,
! 220: u_int16_t proto, const void *data, size_t len)
! 221: {
! 222: struct sockaddr_ng sag;
! 223: u_int16_t hdr[2];
! 224: u_char *buf;
! 225:
! 226: /* Build ng_ppp(4) bypass header */
! 227: hdr[0] = htons(link_num);
! 228: hdr[1] = htons(proto);
! 229:
! 230: /* Set destination hook */
! 231: memset(&sag, 0, sizeof(sag));
! 232: sag.sg_len = 2 + sizeof(NODE_HOOK);
! 233: sag.sg_family = AF_NETGRAPH;
! 234: strlcpy(sag.sg_data, NODE_HOOK, sizeof(sag.sg_data));
! 235:
! 236: /* Write packet */
! 237: if ((buf = MALLOC(TYPED_MEM_TEMP, len + 4)) == NULL)
! 238: return (-1);
! 239: memcpy(buf, hdr, 4);
! 240: memcpy(buf + 4, data, len);
! 241: if (sendto(node->dsock, buf, len + 4, 0,
! 242: (struct sockaddr *)&sag, sag.sg_len) == -1
! 243: && errno != ENOBUFS) {
! 244: FREE(TYPED_MEM_TEMP, buf);
! 245: return (-1);
! 246: }
! 247: FREE(TYPED_MEM_TEMP, buf);
! 248:
! 249: /* Done */
! 250: return (len);
! 251: }
! 252:
! 253: /*
! 254: * Connect a link.
! 255: */
! 256: int
! 257: ppp_node_connect(struct ppp_node *node, u_int link_num,
! 258: const char *path, const char *hook)
! 259: {
! 260: struct ngm_connect connect;
! 261:
! 262: /* Sanity checks */
! 263: if (link_num >= NG_PPP_MAX_LINKS) {
! 264: errno = EINVAL;
! 265: return (-1);
! 266: }
! 267: if (node->connected[link_num])
! 268: ppp_node_disconnect(node, link_num);
! 269:
! 270: /* Connect ng_ppp(4) device hook */
! 271: memset(&connect, 0, sizeof(connect));
! 272: strlcpy(connect.path, path, sizeof(connect.path));
! 273: snprintf(connect.ourhook, sizeof(connect.ourhook),
! 274: "%s%u", NG_PPP_HOOK_LINK_PREFIX, link_num);
! 275: strlcpy(connect.peerhook, hook, sizeof(connect.peerhook));
! 276: if (NgSendMsg(node->csock, NODE_HOOK,
! 277: NGM_GENERIC_COOKIE, NGM_CONNECT, &connect, sizeof(connect)) == -1) {
! 278: LOG(LOG_ERR, "connecting %s%u: %m",
! 279: NG_PPP_HOOK_LINK_PREFIX, link_num);
! 280: return (-1);
! 281: }
! 282:
! 283: /* Done */
! 284: node->connected[link_num] = 1;
! 285: return (0);
! 286: }
! 287:
! 288: /*
! 289: * Disonnect a link.
! 290: */
! 291: int
! 292: ppp_node_disconnect(struct ppp_node *node, u_int link_num)
! 293: {
! 294: struct ngm_rmhook rmhook;
! 295:
! 296: /* Sanity check */
! 297: if (link_num >= NG_PPP_MAX_LINKS) {
! 298: errno = EINVAL;
! 299: return (-1);
! 300: }
! 301:
! 302: /* Remove hook */
! 303: memset(&rmhook, 0, sizeof(rmhook));
! 304: snprintf(rmhook.ourhook, sizeof(rmhook.ourhook),
! 305: "%s%u", NG_PPP_HOOK_LINK_PREFIX, link_num);
! 306: if (NgSendMsg(node->csock, NODE_HOOK, NGM_GENERIC_COOKIE,
! 307: NGM_RMHOOK, &rmhook, sizeof(rmhook)) == -1
! 308: && errno != ENOENT) {
! 309: LOG(LOG_ERR, "disconnecting %s%u: %m",
! 310: NG_PPP_HOOK_LINK_PREFIX, link_num);
! 311: return (-1);
! 312: }
! 313:
! 314: /* Done */
! 315: node->connected[link_num] = 0;
! 316: return (0);
! 317: }
! 318:
! 319: /*
! 320: * Get absolute path to node.
! 321: */
! 322: const char *
! 323: ppp_node_get_path(struct ppp_node *node)
! 324: {
! 325: return (node->path);
! 326: }
! 327:
! 328: /***********************************************************************
! 329: CONTROL MESSAGE FUNCTIONS
! 330: ***********************************************************************/
! 331:
! 332: /*
! 333: * Get node configuration.
! 334: */
! 335: int
! 336: ppp_node_get_config(struct ppp_node *node, struct ng_ppp_node_conf *conf)
! 337: {
! 338: union {
! 339: u_char buf[sizeof(struct ng_mesg) + sizeof(*conf)];
! 340: struct ng_mesg reply;
! 341: } buf;
! 342: struct ng_mesg *const reply = &buf.reply;
! 343:
! 344: if (!node->got_conf) {
! 345: memset(&buf, 0, sizeof(buf));
! 346: if (NgSendMsg(node->csock, NODE_HOOK,
! 347: NGM_PPP_COOKIE, NGM_PPP_GET_CONFIG, NULL, 0) < 0) {
! 348: LOG(LOG_ERR, "%s: %m", "getting node configuration");
! 349: return (-1);
! 350: }
! 351: if (NgRecvMsg(node->csock, reply, sizeof(buf), NULL) == -1) {
! 352: LOG(LOG_ERR, "%s: %m", "receiving node configuration");
! 353: return (-1);
! 354: }
! 355: memcpy(&node->conf, reply->data, sizeof(node->conf));
! 356: node->got_conf = 1;
! 357: }
! 358: *conf = node->conf;
! 359: return (0);
! 360: }
! 361:
! 362: /*
! 363: * Set node configuration.
! 364: */
! 365: int
! 366: ppp_node_set_config(struct ppp_node *node, const struct ng_ppp_node_conf *conf)
! 367: {
! 368: if (NgSendMsg(node->csock, NODE_HOOK, NGM_PPP_COOKIE,
! 369: NGM_PPP_SET_CONFIG, conf, sizeof(*conf)) < 0) {
! 370: LOG(LOG_ERR, "%s: %m", "configuring node");
! 371: return (-1);
! 372: }
! 373: node->got_conf = 1;
! 374: node->conf = *conf;
! 375: return (0);
! 376: }
! 377:
! 378: /*
! 379: * Set incoming packet recipient.
! 380: */
! 381: void
! 382: ppp_node_set_recv(struct ppp_node *node, ppp_node_recv_t *recv, void *arg)
! 383: {
! 384: node->recv = recv;
! 385: node->rarg = arg;
! 386: }
! 387:
! 388: /*
! 389: * Set incoming message recipient.
! 390: */
! 391: int
! 392: ppp_node_set_recvmsg(struct ppp_node *node, u_int32_t cookie,
! 393: u_int32_t cmd, ppp_node_recvmsg_t *recvmsg, void *arg)
! 394: {
! 395: struct ppp_node_recvmsg *rm;
! 396: int found = 0;
! 397:
! 398: /* Check if already exists */
! 399: TAILQ_FOREACH(rm, &node->rmlist, next) {
! 400: if (rm->cookie == cookie && rm->cmd == cmd) {
! 401: found = 1;
! 402: break;
! 403: }
! 404: }
! 405:
! 406: /* Removing it? */
! 407: if (recvmsg == NULL) {
! 408: if (found) {
! 409: TAILQ_REMOVE(&node->rmlist, rm, next);
! 410: FREE(NODE_MTYPE, rm);
! 411: }
! 412: return (0);
! 413: } else if (found) {
! 414: errno = EALREADY;
! 415: return (-1);
! 416: }
! 417:
! 418: /* Add new message receiver */
! 419: if ((rm = MALLOC(NODE_MTYPE, sizeof(*rm))) == NULL)
! 420: return (-1);
! 421: memset(rm, 0, sizeof(*rm));
! 422: rm->cookie = cookie;
! 423: rm->cmd = cmd;
! 424: rm->recvmsg = recvmsg;
! 425: rm->arg = arg;
! 426: TAILQ_INSERT_TAIL(&node->rmlist, rm, next);
! 427: return (0);
! 428: }
! 429:
! 430: int
! 431: ppp_node_send_msg(struct ppp_node *node, const char *relpath,
! 432: u_int32_t cookie, u_int32_t cmd, const void *payload, size_t plen)
! 433: {
! 434: char path[NG_PATHSIZ];
! 435:
! 436: if (relpath == NULL)
! 437: strlcpy(path, NODE_HOOK, sizeof(path));
! 438: else
! 439: snprintf(path, sizeof(path), "%s.%s", NODE_HOOK, relpath);
! 440: if (NgSendMsg(node->csock, path, cookie, cmd, payload, plen) == -1)
! 441: return (-1);
! 442: return (0);
! 443: }
! 444:
! 445: int
! 446: ppp_node_recv_msg(struct ppp_node *node,
! 447: struct ng_mesg *msg, size_t mlen, char *raddr)
! 448: {
! 449: return (NgRecvMsg(node->csock, msg, mlen, raddr));
! 450: }
! 451:
! 452: /***********************************************************************
! 453: INTERNAL FUNCTIONS
! 454: ***********************************************************************/
! 455:
! 456: /*
! 457: * Handle incoming data on the ng_ppp(4) bypass hook.
! 458: */
! 459: static void
! 460: ppp_node_read_packet(void *arg)
! 461: {
! 462: struct ppp_node *const node = arg;
! 463: u_char buf[4 + PKTBUFLEN];
! 464: u_int16_t link_num;
! 465: u_int16_t proto;
! 466: int len;
! 467:
! 468: /* Read packet */
! 469: if ((len = read(node->dsock, buf, sizeof(buf))) == -1) {
! 470: LOG(LOG_ERR, "error reading bypass hook: %m");
! 471: return;
! 472: }
! 473:
! 474: /* Extract protocol and link number */
! 475: if (len < 4) {
! 476: LOG(LOG_ERR, "read len %d bypass packet", len);
! 477: return;
! 478: }
! 479: memcpy(&link_num, buf, 2);
! 480: link_num = ntohs(link_num);
! 481: memcpy(&proto, buf + 2, 2);
! 482: proto = ntohs(proto);
! 483:
! 484: /* Deliver packet */
! 485: if (node->recv != NULL)
! 486: (*node->recv)(node->rarg, link_num, proto, buf + 4, len - 4);
! 487: }
! 488:
! 489: /*
! 490: * Handle incoming control message on the netgraph socket.
! 491: */
! 492: static void
! 493: ppp_node_read_message(void *arg)
! 494: {
! 495: struct ppp_node *const node = arg;
! 496: const size_t max_msglen = 4096;
! 497: char raddr[NG_PATHSIZ];
! 498: struct ppp_node_recvmsg *rm;
! 499: struct ng_mesg *msg;
! 500: int found = 0;
! 501: int len;
! 502:
! 503: /* Allocate buffer */
! 504: if ((msg = MALLOC(TYPED_MEM_TEMP, max_msglen)) == NULL) {
! 505: LOG(LOG_ERR, "malloc: %m");
! 506: return;
! 507: }
! 508:
! 509: /* Read incoming message */
! 510: if ((len = NgRecvMsg(node->csock, msg, max_msglen, raddr)) == -1) {
! 511: LOG(LOG_ERR, "reading ctrl message: %m");
! 512: goto done;
! 513: }
! 514:
! 515: /* Search for handler */
! 516: TAILQ_FOREACH(rm, &node->rmlist, next) {
! 517: if (rm->cookie == msg->header.typecookie
! 518: && rm->cmd == msg->header.cmd) {
! 519: found = 1;
! 520: break;
! 521: }
! 522: }
! 523: if (!found)
! 524: goto done;
! 525:
! 526: /* Invoke handler */
! 527: (*rm->recvmsg)(rm->arg, msg);
! 528:
! 529: done:
! 530: /* Clean up */
! 531: FREE(TYPED_MEM_TEMP, msg);
! 532: }
! 533:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>