/* wrnad_mon.c - Wind River network acceleration daemon (NAD) */ /* * Copyright (c) 2010 Wind River Systems, Inc. * * The right to copy, distribute or otherwise make use of this software * may be licensed only pursuant to the terms of an applicable Wind River * license agreement. */ /* includes */ #include #include #include #include #include #include #include #include "wrnad.h" /* for VLAN stacking */ #include "wrnad_svconfig.h" /* defines */ /* * FIXME: The stdlib.h at ipxinc/include/stdlib.h doesn't define * EXIT_FAILURE or EXIT_SUCCESS. */ #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif /* typedefs */ /* imports */ /* globals */ Wrnad_proxy_interface wrnad_proxy_ifs[WRNAD_MAX_IF]; int wrnad_rtfd = -1; /* for clean-up */ int wrnad_mipcfd = -1; int wrnad_pfkeyfd = -1; /* locals */ static int wrnad_seq = 0; static int request_done = 0; static char recvbuf[WRNAD_RECVBUF_SIZE]; int wrnad_mon_running = 0; /* * Table to translate Linux address family values * to IPNET/VxWorks (NAE) address family values. * Gotta love them family values! */ #if !defined IPWRNAD /* In IPWRNAD case, we use ipxinc/include/sys/socket.h ... */ #define IP_AF_UNSPEC 0 /* Unspecified domain */ #define IP_AF_INET 2 /* IPv4 */ #define IP_AF_IPX 4 /* IPX */ #define IP_AF_NETLINK 16 /* Linux Netlink, RFC 3549 */ #define IP_AF_ROUTE 17 /* Internal routing Protocol */ #define IP_AF_LINK 18 /* Link layer address (used in route entries and ancillary data) */ #define IP_AF_PACKET 19 /* Linux packet family */ /* for VxWorks (NAE) case: */ #define IP_AF_INET6 28 /* Default family value of IP version 6 */ #define IP_AF_KEY 27 /* PFKEYv2 key-management socket API */ #define IP_AF_TIPC 33 /* Transparent Inter-Process Communic. */ #define IP_AF_MAX 35 static unsigned char af_xlat[AF_MAX] = { IP_AF_UNSPEC, /* 0, AF_UNSPEC */ 255, /* 1, AF_LOCAL/AF_UNIX/AF_FILE */ IP_AF_INET, /* 2, AF_INET */ 255, /* 3, AF_AX25 */ IP_AF_IPX, /* 4, AF_IPX */ 255, /* 5, AF_APPLETALK */ 255, /* 6, AF_NETROM */ 255, /* 7, AF_BRIDGE */ 255, /* 8, AF_ATMPVC */ 255, /* 9, AF_X25 */ IP_AF_INET6, /* 10, AF_INET6 */ 255, /* 11, AF_ROSE */ 255, /* 12, AF_DECnet */ 255, /* 13, AF_NETBEUI */ 255, /* 14, AF_SECURITY */ IP_AF_KEY, /* 15, AF_KEY */ IP_AF_NETLINK, /* 16, AF_NETLINK (AF_ROUTE is an alias in linux) */ IP_AF_PACKET, /* 17, AF_PACKET */ 255, /* 18, AF_ASH */ 255, /* 19, AF_ECONET */ 255, /* 20, AF_ATMSVC */ 255, /* 21, AF_RDS */ 255, /* 22, AF_SNA */ 255, /* 23, AF_IRDA */ 255, /* 24, AF_PPPOX */ 255, /* 25, AF_WANPIPE */ 255, /* 26, AF_LLC */ 255, /* 27, unused */ 255, /* 28, unused */ 255, /* 29, AF_CAN */ IP_AF_TIPC, /* 30, AF_TIPC */ 255, /* 31, AF_BLUETOOTH */ 255, /* 32, AF_IUCV */ 255, /* 33, AF_RXRPC */ 255, /* 34, AF_ISDN */ 255, /* 35, AF_PHONET */ 255 /* 36, AF_IEEE802154 */ /* 37 --> AF_MAX */ }; #endif /* !defined(IPWRNAD) */ /******************************************************************************* * * wrnad_rtnl_init - initialize wrnad monitoring * * This routine initializes wrnad monitoring * * RETURNS: N/A * * ERRNO: N/A */ static void wrnad_rtnl_init(void) { memset(&wrnad_proxy_ifs, 0, sizeof(wrnad_proxy_ifs)); } /******************************************************************************* * * wrnad_rtnl_control_handler - rtnetlink handler for control messages * * This routine handles rtnetlink control messages. * * RETURNS: -1 = failed * 0 = skip * 1 = action * * ERRNO: N/A */ static int wrnad_rtnl_control_handler(struct nlmsghdr *nlh) { int len; struct nlmsgerr *err; if (nlh->nlmsg_type == NLMSG_ERROR) { err = NLMSG_DATA(nlh); len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct nlmsgerr)); if (len < 0) { wrnad_log(LOG_WARNING, "Rtnetlink message type %s has wrong nlmsg length %d!", wrnad_rtnl_typestr(nlh->nlmsg_type), nlh->nlmsg_len); return -1; } #ifndef IPWRNAD /* Neighbour updates */ if (err->msg.nlmsg_type != RTM_NEWNEIGH) #endif wrnad_log(LOG_ERR, "Rtnetlink control message ERROR: error=%s (%d)", strerror(-err->error), -err->error); } else if (nlh->nlmsg_type == NLMSG_NOOP) wrnad_log(LOG_DEBUG, "Rtnetlink control message NOOP:"); else if (nlh->nlmsg_type == NLMSG_DONE) { request_done = 1; wrnad_log(LOG_DEBUG, "Rtnetlink control message DONE:"); } else wrnad_log(LOG_WARNING, "Rtnetlink control message %d:", nlh->nlmsg_type); return 0; } /******************************************************************************* * * wrnad_rtnl_handler - find rtnetlink handler * * This routine returns a handler form the rtnetlink message type. * * RETURNS: handler or NULL if not found * * ERRNO: N/A */ static wrnad_rtnl_handler wrnad_rtnl_find_handler(struct nlmsghdr *nlh) { switch (nlh->nlmsg_type) { case NLMSG_ERROR: case NLMSG_NOOP: case NLMSG_DONE: return wrnad_rtnl_control_handler; case RTM_NEWNEIGH: case RTM_DELNEIGH: return wrnad_rtnl_neigh_handler; case RTM_NEWLINK: case RTM_DELLINK: return wrnad_rtnl_link_handler; case RTM_NEWADDR: case RTM_DELADDR: return wrnad_rtnl_addr_handler; case RTM_NEWROUTE: case RTM_DELROUTE: return wrnad_rtnl_route_handler; /* for VLAN stacking */ case RTM_SET_SVLAN: case RTM_DEL_SVLAN: return wrnad_rtnl_svlan_handler; default: return NULL; } } /******************************************************************************* * * wrnad_rtnl_close - close rtnetlink socket * * This routine closes a rtnetlink socket * * RETURNS: N/A * * ERRNO: N/A */ static void wrnad_rtnl_close(int fd) { if (fd >= 0) close(fd); } /******************************************************************************* * * wrnad_rtnl_open - open and bind rtnetlink socket * * This routine opens a rtnetlink socket and binds it. * * RETURNS: socket descriptor if exited normally, else -1. * * ERRNO: N/A */ static int wrnad_rtnl_open(int proto, unsigned int groups, struct sockaddr_nl *laddr) { int fd; socklen_t addr_len; int bufsize; socklen_t slen; fd = socket(AF_NETLINK, SOCK_RAW, proto); if (fd < 0) { wrnad_log(LOG_ERR, "Failed to open rtnetlink socket!"); return -1; } wrnad_log(LOG_DEBUG, "Rtnetlink socket %d created.", fd); memset(laddr, 0, sizeof(laddr)); laddr->nl_family = AF_NETLINK; laddr->nl_groups = groups; /* associate the process's pid with netlink sock address */ laddr->nl_pid = getpid(); if (bind(fd, (struct sockaddr *)laddr, sizeof(struct sockaddr_nl)) < 0) { wrnad_log(LOG_ERR, "Failed to bind rtnetlink socket! errno=%s", strerror(errno)); close(fd); return -1; } addr_len = sizeof(struct sockaddr_nl); if (getsockname(fd, (struct sockaddr *)laddr, &addr_len) < 0) { wrnad_log(LOG_ERR, "Failed to getsockname!"); close(fd); return -1; } bufsize = 0; slen = (socklen_t)sizeof (bufsize); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, &slen) < 0) wrnad_log(LOG_ERR, "Failed to get default netlink socket RCVBUF\n"); else wrnad_log(LOG_INFO, "Default netlink socket RCVBUF is %u\n", bufsize); bufsize = 64 * 1024 * 1024; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) < 0) { wrnad_log(LOG_ERR, "Failed to set netlink socket RCVBUF to %u.\n", bufsize); close(fd); return -1; } wrnad_log(LOG_DEBUG, "Rtnetlink socket %d bound to pid %u.", fd, laddr->nl_pid); return fd; } /******************************************************************************* * * wrnad_rtnl_parse - parse message received on rtnetlink socket * * This routine parses indivudual messages received on a rtnetlink socket * * RETURNS: 0 if exited normally, else -1. * * ERRNO: N/A */ static int wrnad_rtnl_parse(int fd, struct nlmsghdr *nlh) { wrnad_rtnl_handler f; int ret; /* Find handler */ if ((f = wrnad_rtnl_find_handler(nlh))) { ret = f(nlh); if (ret == 1) { /* Send rtnetlink message over MIPC */ nlh->nlmsg_seq = 0; nlh->nlmsg_pid = 0; ret = wrnad_mipc_send_netlink(fd, nlh); } return ret; } else { wrnad_log(LOG_INFO, "No handler found for rtnetlink type %d (%s)!", nlh->nlmsg_type, wrnad_rtnl_typestr(nlh->nlmsg_type)); return -1; } } /******************************************************************************* * * wrnad_rtnl_scan - scan message received on rtnetlink socket * * This routine scans messages received on a rtnetlink socket * * RETURNS: 0 if exited normally, else -1. * * ERRNO: N/A */ static int wrnad_rtnl_scan(char *buf, int buflen, int fd) { struct nlmsghdr *nlh; nlh = (struct nlmsghdr *)buf; while (NLMSG_OK(nlh, (size_t)buflen)) { wrnad_log(LOG_DEBUG, "Rtnetlink message header: length=%d, type=%s, flags=%d, seq=%d, pid=%u.", nlh->nlmsg_len, wrnad_rtnl_typestr(nlh->nlmsg_type), nlh->nlmsg_flags, nlh->nlmsg_seq, nlh->nlmsg_pid); if (wrnad_rtnl_parse(fd, nlh) >= 0) { wrnad_log(LOG_DEBUG, "Rtnetlink message type %s handled ok.", wrnad_rtnl_typestr(nlh->nlmsg_type)); } nlh = NLMSG_NEXT(nlh, buflen); } if (buflen) { wrnad_log(LOG_WARNING, "Rtnetlink message truncated! Found %d trailing bytes.", buflen); return -1; } return 0; } /******************************************************************************* * * wrnad_rtnl_recvfrom - recv on rtnetlink socket * * This routine receives messages on a rtnetlink socket * * RETURNS: 0 if exited normally, else -1. * * ERRNO: N/A */ static int wrnad_rtnl_recvfrom(int rtfd, int mipc_fd) { int len; struct sockaddr_nl raddr; socklen_t addrlen; memset(&raddr, 0, sizeof(raddr)); raddr.nl_family = AF_NETLINK; raddr.nl_pid = 0; raddr.nl_groups = 0; addrlen = 0; len = recvfrom(rtfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&raddr, &addrlen); if (len < 0) { wrnad_log(LOG_ERR, "Recvfrom on rtnetlink socket %d interrupted by error %s -> exit.", rtfd, strerror(errno)); return -1; } else if (len == 0) { wrnad_log(LOG_ERR, "Recvfrom on rtnetlink socket %d interrupted by EOF.", rtfd); return -1; } wrnad_log(LOG_DEBUG, "Recvfrom on rtnetlink socket %d returned %d bytes of data.", rtfd, len); /* Verify that the message was received from the kernel. Note: A message may also arrive from svconfig for VLAN stacking configuration (via the kernel). */ if (raddr.nl_pid) { wrnad_log(LOG_WARNING, "Skipped rtnetlink message received from pid %d!", raddr.nl_pid); return 0; } /* Scan receive message */ if (wrnad_rtnl_scan(recvbuf, len, mipc_fd) < 0) wrnad_log(LOG_WARNING, "Rtnetlink message failed scan!\n"); else wrnad_log(LOG_DEBUG, "Rtnetlink message scanned ok.\n"); return 0; } /******************************************************************************* * * wrnad_rtnl_get_request - set get rtnetlink get request * * This routine sends a request to retrieve all entries of a rtnetlink type. * * RETURNS: 0 if exited normally, else -1. * * ERRNO: N/A */ static int wrnad_rtnl_send_request(int fd, int family, int type) { struct sockaddr_nl raddr; struct { struct nlmsghdr nlh; unsigned char family; } req; memset(&raddr, 0, sizeof(raddr)); raddr.nl_family = AF_NETLINK; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = type; req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; req.nlh.nlmsg_seq = ++wrnad_seq; req.family = family; /* Send the request */ wrnad_log(LOG_DEBUG, "Sending rtnetlink %s request on socket %d", wrnad_rtnl_typestr(type), fd); if (sendto(fd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&raddr, sizeof(struct sockaddr_nl)) < 0) { wrnad_log(LOG_ERR, "send failed! errno=%s (%d)\n", strerror(errno), errno); return -1; } request_done = 0; return 0; } /******************************************************************************* * * wrnad_rtnl_proxyifstr - find proxy interfaces * * This routine returns the current proxy interfaces as a string * * RETURNS: string representation of procy interfaces. * * ERRNO: N/A */ static char * wrnad_rtnl_proxyifstr(char *ifstr) { int i, j; ifstr[0] = '\0'; j = 0; for (i = 0; i 0) FD_SET(pfkeyfd, &rfds); #endif /* !IPWRNAD || IPIPSEC2 */ nfds = rtfd; #if !defined(IPWRNAD) || defined(IPIPSEC2) if (pfkeyfd > nfds) nfds = pfkeyfd; #endif #if !defined(IPWRNAD) if (-1 != ndp) { FD_SET(ndp, &rfds); if (ndp > nfds) nfds = ndp; } #endif ret = select(nfds + 1, &rfds, NULL, NULL, NULL); if (ret < 0) { wrnad_log(LOG_ERR, "select interrupted by error %s.", strerror(errno)); if (errno == EINTR || errno == EAGAIN) continue; ret = 0; break; } else if (ret == 0) { wrnad_log(LOG_ERR, "select returned unexpected timeout", strerror(errno)); ret = -1; goto exit; } if (FD_ISSET(rtfd, &rfds)) { /* Receive messages on the rtnetlink socket */ if (wrnad_rtnl_recvfrom(rtfd, mipcfd) < 0) { ret = -1; goto exit; } } #if !defined(IPWRNAD) if (-1 != ndp && FD_ISSET(ndp, &rfds)) wrnad_neigh_ndp_read(ndp, rtfd, recvbuf, sizeof(recvbuf)); #endif #if !defined(IPWRNAD) || defined(IPIPSEC2) if (FD_ISSET(pfkeyfd, &rfds)) { /* Receive messages on the pfkey socket */ if (wrnad_pfkey_recv(pfkeyfd, mipcfd) < 0) { ret = -1; goto exit; } } #endif /* !IPWRNAD || IPIPSEC2 */ } exit: wrnad_mon_running = 0; return ret; } void wrnad_cleanup(void) { int i; wrnad_mipc_close(wrnad_mipcfd); wrnad_mipcfd = -1; wrnad_rtnl_close(wrnad_rtfd); wrnad_rtfd = -1; #if !defined(IPWRNAD) || defined(IPIPSEC2) wrnad_pfkey_close(wrnad_pfkeyfd); wrnad_pfkeyfd = -1; #endif /* !IPWRNAD || IPIPSEC2 */ #ifdef WRNAD_IDENTIFY_ROUTERS for (i = 0; i= AF_MAX) return (unsigned char)255; return af_xlat[linux_af]; } #endif /* !defined WRNAD */