static int nlmsg_receive(char *buf, int len, int (*cb)(struct nlmsghdr *, void *), int (*err_cb)(int, void *), void *arg) { struct nlmsghdr *hdr; for (hdr = (struct nlmsghdr *)buf; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { if (hdr->nlmsg_seq != CR_NLMSG_SEQ) continue; if (hdr->nlmsg_type == NLMSG_DONE) { int *len = (int *)NLMSG_DATA(hdr); if (*len < 0) { pr_err("ERROR %d reported by netlink (%s)\n", *len, strerror(-*len)); return *len; } return 0; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (hdr->nlmsg_len - sizeof(*hdr) < sizeof(struct nlmsgerr)) { pr_err("ERROR truncated\n"); return -1; } if (err->error == 0) return 0; return err_cb(err->error, arg); } if (cb(hdr, arg)) return -1; } return 1; }
int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId){ struct nlmsghdr *nlHdr; int readLen = 0, msgLen = 0; do{ /* Recieve response from the kernel */ if((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0){ perror("SOCK READ: "); return -1; } nlHdr = (struct nlmsghdr *)bufPtr; /* Check if the header is valid */ if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) { perror("Error in recieved packet"); return -1; } /* Check if the its the last message */ if(nlHdr->nlmsg_type == NLMSG_DONE) { break; } else{ /* Else move the pointer to buffer appropriately */ bufPtr += readLen; msgLen += readLen; } /* Check if its a multi part message */ if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) { /* return if its not */ break; } } while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); return msgLen; }
static struct nlmsghdr *get_msg(void) { struct nlmsghdr *nlh; int len; nlh = (struct nlmsghdr *)malloc(MAX_MSG_SIZE); if (NULL==nlh) return NULL; memset(nlh, 0, MAX_MSG_SIZE); len = recv(nl_sd, (void *)nlh, MAX_MSG_SIZE, 0); if (len < 0) { printf("RECEIVE FAILED with %d", errno); free(nlh); return NULL; } if (!(NLMSG_OK(nlh, (unsigned int)len))) { printf("RECEIVE FAILED, message too long\n"); free(nlh); return NULL; } if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(nlh); printf("RECEIVE FAILED with msg error %i (pid %d): %s\n", err->error, nlh->nlmsg_pid, strerror(err->error * -1)); if (hexdump && len > 0) hexprint((char *)nlh, len); free(nlh); return NULL; } if (hexdump) { printf("RECEIVED A MESSAGE: %d\n", len); hexprint((char *)nlh, nlh->nlmsg_len); } return nlh; }
void process_netlink_msg(int sock) { int len; char buf[4096]; struct iovec iov = { buf, sizeof(buf) }; struct sockaddr_nl sa; struct msghdr msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; struct nlmsghdr *nh; struct ifinfomsg * ifinfo; char ifname[IF_NAMESIZE] = {""}; len = recvmsg (sock, &msg, 0); if (len == -1) { flog(LOG_ERR, "recvmsg failed: %s", strerror(errno)); } for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len); nh = NLMSG_NEXT (nh, len)) { /* The end of multipart message. */ if (nh->nlmsg_type == NLMSG_DONE) return; if (nh->nlmsg_type == NLMSG_ERROR) { flog(LOG_ERR, "%s:%d Some type of netlink error.\n", __FILE__, __LINE__); abort(); } /* Continue with parsing payload. */ ifinfo = NLMSG_DATA(nh); if_indextoname(ifinfo->ifi_index, ifname); if (ifinfo->ifi_flags & IFF_RUNNING) { dlog(LOG_DEBUG, 3, "%s, ifindex %d, flags is running", ifname, ifinfo->ifi_index); } else { dlog(LOG_DEBUG, 3, "%s, ifindex %d, flags is *NOT* running", ifname, ifinfo->ifi_index); } reload_config(); } }
void netlink_multicast(void) { ssize_t len; struct nlmsghdr *h; int flags; /* don't risk blocking reading netlink messages here. */ if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) return; if ((len = netlink_recv()) != -1) { for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) if (h->nlmsg_type == NLMSG_ERROR) nl_err(h); else nl_routechange(h); } /* restore non-blocking status */ fcntl(daemon->netlinkfd, F_SETFL, flags); }
/* * parse_netlink_msg */ void parse_netlink_msg (char *buf, size_t buf_len) { netlink_msg_ctx_t ctx_space, *ctx; struct nlmsghdr *hdr; int status; int len; ctx = &ctx_space; hdr = (struct nlmsghdr *) buf; len = buf_len; for (; NLMSG_OK (hdr, len); hdr = NLMSG_NEXT(hdr, len)) { netlink_msg_ctx_init(ctx); ctx->hdr = (struct nlmsghdr *) buf; switch (hdr->nlmsg_type) { case RTM_DELROUTE: case RTM_NEWROUTE: parse_route_msg(ctx); if (ctx->err_msg) { err_msg("Error parsing route message: %s", ctx->err_msg); } print_netlink_msg_ctx(ctx); break; default: trace(1, "Ignoring unknown netlink message - Type: %d", hdr->nlmsg_type); } netlink_msg_ctx_cleanup(ctx); } }
void adapterChangeObserverThread(void* aPtr) { InterfaceChangedObserver* observer = (InterfaceChangedObserver*) aPtr; OsNetworkHandle *handle = observer->netHnd; char buffer[4096]; struct nlmsghdr *nlh; int32_t len, ret; fd_set rfds,errfds; while (1) { if (SocketInterrupted(handle)) { return; } FD_ZERO(&rfds); FD_SET(handle->iPipe[0], &rfds); FD_SET(handle->iSocket, &rfds); FD_ZERO(&errfds); FD_SET(handle->iSocket, &errfds); ret = TEMP_FAILURE_RETRY_2(select(nfds(handle), &rfds, NULL, &errfds, NULL), handle); if ((ret > 0) && FD_ISSET(handle->iSocket, &rfds)) { nlh = (struct nlmsghdr *) buffer; if ((len = recv(handle->iSocket, nlh, 4096, 0)) > 0) { while (NLMSG_OK(nlh, len) && (nlh->nlmsg_type != NLMSG_DONE)) { if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR || nlh->nlmsg_type == RTM_NEWLINK) { observer->iCallback(observer->iArg); } nlh = NLMSG_NEXT(nlh, len); } } } } }
static int interpret(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_links, struct ifaddrs **p_resultList) { pid_t l_pid = getpid(); for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { unsigned int l_nlsize = p_netlinkList->m_size; struct nlmsghdr *l_hdr; for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } if(l_hdr->nlmsg_type == NLMSG_DONE) { break; } if(l_hdr->nlmsg_type == RTM_NEWLINK) { if (interpretLink(l_hdr, p_links, p_resultList) == -1) { return -1; } } else if(l_hdr->nlmsg_type == RTM_NEWADDR) { if (interpretAddr(l_hdr, p_links, p_resultList) == -1) { return -1; } } } } return 0; }
/** * nfnl_process - process data coming from a nfnetlink system * @h: nfnetlink handler * @buf: buffer that contains the netlink message * @len: size of the data contained in the buffer (not the buffer size) * * This function processes all the nfnetlink messages contained inside a * buffer. It performs the appropiate sanity checks and passes the message * to a certain handler that is registered via register_callback(). * * On success, NFNL_CB_STOP is returned if the data processing has finished. * If a value NFNL_CB_CONTINUE is returned, then there is more data to * process. On error, NFNL_CB_CONTINUE is returned and errno is set to the * appropiate value. * * In case that the callback returns NFNL_CB_FAILURE, errno may be set by * the library client. If your callback decides not to process data anymore * for any reason, then it must return NFNL_CB_STOP. Otherwise, if the * callback continues the processing NFNL_CB_CONTINUE is returned. */ int nfnl_process(struct nfnl_handle *h, const unsigned char *buf, size_t len) { int ret = 0; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; assert(h); assert(buf); assert(len > 0); /* check for out of sequence message */ if (nlh->nlmsg_seq && nlh->nlmsg_seq != h->seq) { errno = EILSEQ; return -1; } while (len >= NLMSG_SPACE(0) && NLMSG_OK(nlh, len)) { ret = nfnl_step(h, nlh); if (ret <= NFNL_CB_STOP) break; nlh = NLMSG_NEXT(nlh, len); } return ret; }
void ifup_scan_event(struct nlmsghdr *nh, int len, int *seen_flags) { struct ifinfomsg *ifi; for (;NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { switch (nh->nlmsg_type) { case NLMSG_DONE: return; case NLMSG_ERROR: return; case RTM_NEWLINK: ifi = NLMSG_DATA(nh); if (ifi->ifi_flags & IFF_RUNNING) { *seen_flags |= SEEN_RUNNING; } break; case RTM_NEWADDR: *seen_flags |= SEEN_ADDRESS; break; } } }
static void netlink_new_msg(struct uloop_fd *ufd, unsigned events) { struct nlmsghdr *nlh; char buffer[BUFSIZ]; int msg_size; memset(&buffer, 0, sizeof(buffer)); nlh = (struct nlmsghdr *)buffer; if ((msg_size = recv(ufd->fd, nlh, BUFSIZ, 0)) == -1) { DD("error receiving netlink message"); return; } while (msg_size > sizeof(*nlh)) { int len = nlh->nlmsg_len; int req_len = len - sizeof(*nlh); if (req_len < 0 || len > msg_size) { DD("error reading netlink message"); return; } if (!NLMSG_OK(nlh, msg_size)) { DD("netlink message is not NLMSG_OK"); return; } if (nlh->nlmsg_type == RTM_NEWADDR) easycwmp_netlink_interface(nlh); msg_size -= NLMSG_ALIGN(len); nlh = (struct nlmsghdr*)((char*)nlh + NLMSG_ALIGN(len)); } }
static int get_netlink(int fd, int flags, int (*callback)(struct nlmsghdr *, const char *), const char *ifname) { char *buffer = NULL; ssize_t bytes; struct nlmsghdr *nlm; int r = -1; buffer = xzalloc(sizeof(char) * BUFFERLEN); for (;;) { bytes = recv(fd, buffer, BUFFERLEN, flags); if (bytes == -1) { if (errno == EAGAIN) { r = 0; goto eexit; } if (errno == EINTR) continue; goto eexit; } for (nlm = (struct nlmsghdr *)buffer; NLMSG_OK(nlm, (size_t)bytes); nlm = NLMSG_NEXT(nlm, bytes)) { r = callback(nlm, ifname); if (r != 0) goto eexit; } } eexit: free(buffer); return r; }
int nl_send(struct nl_handle *hnd, struct iovec *iov, int iovlen) { struct sockaddr_nl sa = { .nl_family = AF_NETLINK, }; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = iov, .msg_iovlen = iovlen, }; struct nlmsghdr *src = iov->iov_base; src->nlmsg_seq = ++hnd->seq; if (sendmsg(hnd->fd, &msg, 0) < 0) return errno; return 0; } int nl_recv(struct nl_handle *hnd, struct nlmsg_entry **dest, int is_dump) { struct sockaddr_nl sa = { .nl_family = AF_NETLINK, }; struct iovec iov; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = &iov, .msg_iovlen = 1, }; char buf[16384]; int len, err; struct nlmsghdr *n; struct nlmsg_entry *ptr = NULL; /* GCC false positive */ struct nlmsg_entry *entry; *dest = NULL; while (1) { iov.iov_base = buf; iov.iov_len = sizeof(buf); len = recvmsg(hnd->fd, &msg, 0); if (len < 0) return errno; if (!len) return EPIPE; if (sa.nl_pid) { /* not from the kernel */ continue; } for (n = (struct nlmsghdr *)buf; NLMSG_OK(n, len); n = NLMSG_NEXT(n, len)) { if (n->nlmsg_pid != hnd->pid || n->nlmsg_seq != hnd->seq) continue; if (is_dump && n->nlmsg_type == NLMSG_DONE) return 0; if (n->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(n); err = -nlerr->error; goto err_out; } entry = malloc(n->nlmsg_len + sizeof(void *)); if (!entry) { err = ENOMEM; goto err_out; } entry->next = NULL; memcpy(&entry->h, n, n->nlmsg_len); if (!*dest) *dest = entry; else ptr->next = entry; ptr = entry; if (!is_dump) return 0; } } err_out: nlmsg_free(*dest); *dest = NULL; return err; } int nl_exchange(struct nl_handle *hnd, struct nlmsghdr *src, struct nlmsg_entry **dest) { struct iovec iov = { .iov_base = src, .iov_len = src->nlmsg_len, }; int is_dump; int err; is_dump = !!(src->nlmsg_flags & NLM_F_DUMP); err = nl_send(hnd, &iov, 1); if (err) return err; return nl_recv(hnd, dest, is_dump); } /* The original payload is not freed. Returns 0 in case of error, length * of *dest otherwise. *dest is newly allocated. */ int nla_add_str(void *orig, int orig_len, int nla_type, const char *str, void **dest) { struct nlattr *nla; int len = strlen(str) + 1; int size; size = NLA_ALIGN(orig_len) + NLA_HDRLEN + NLA_ALIGN(len); *dest = calloc(size, 1); if (!*dest) return 0; if (orig_len) memcpy(*dest, orig, orig_len); nla = *dest + NLA_ALIGN(orig_len); nla->nla_len = NLA_HDRLEN + len; nla->nla_type = nla_type; memcpy(nla + 1, str, len); return size; } int rtnl_open(struct nl_handle *hnd) { return nl_open(hnd, NETLINK_ROUTE); } int rtnl_dump(struct nl_handle *hnd, int family, int type, struct nlmsg_entry **dest) { struct { struct nlmsghdr n; struct ifinfomsg i; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = sizeof(req); req.n.nlmsg_type = type; req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; req.i.ifi_family = family; return nl_exchange(hnd, &req.n, dest); } void rtnl_parse(struct rtattr *tb[], int max, struct rtattr *rta, int len) { memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); while (RTA_OK(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; rta = RTA_NEXT(rta, len); } } void rtnl_parse_nested(struct rtattr *tb[], int max, struct rtattr *rta) { rtnl_parse(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); } int genl_open(struct nl_handle *hnd) { return nl_open(hnd, NETLINK_GENERIC); } int genl_request(struct nl_handle *hnd, int type, int cmd, void *payload, int payload_len, struct nlmsg_entry **dest) { struct { struct nlmsghdr n; struct genlmsghdr g; } req; struct iovec iov[2]; int err; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = sizeof(req) + payload_len; req.n.nlmsg_type = type; req.n.nlmsg_flags = NLM_F_REQUEST; req.g.cmd = cmd; req.g.version = 1; iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); iov[1].iov_base = payload; iov[1].iov_len = payload_len; err = nl_send(hnd, iov, 2); if (err) return err; return nl_recv(hnd, dest, 0); } unsigned int genl_family_id(struct nl_handle *hnd, const char *name) { unsigned int res = 0; struct nlattr *nla; int len; struct nlmsg_entry *dest; void *ptr; len = nla_add_str(NULL, 0, CTRL_ATTR_FAMILY_NAME, name, &ptr); if (!len) return 0; if (genl_request(hnd, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, ptr, len, &dest)) { free(ptr); return 0; } free(ptr); len = dest->h.nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; ptr = (void *)&dest->h + NLMSG_HDRLEN + GENL_HDRLEN; while (len > NLA_HDRLEN) { nla = ptr; if (nla->nla_type == CTRL_ATTR_FAMILY_ID && nla->nla_len >= NLA_HDRLEN + 2) { res = *(uint16_t *)(nla + 1); break; } ptr += NLMSG_ALIGN(nla->nla_len); len -= NLMSG_ALIGN(nla->nla_len); } nlmsg_free(dest); return res; }
//send data to command socket and wait - blocking - for a reply (an error message or a data dump) int KernelNetlinkProtocol::send_and_parse(const std::string& data) { int ret = send_to(cmd_sock_, data, cmd_tx_buff_size_, reinterpret_cast<sockaddr*>(&cmd_peer_addr_)); if (ret <= 0) { tnt::Log::warning("KernelNetlinkProtocol::send_and_parse: send_to returned ", ret); return ret; } //tnt::Log::info(colors::green, "\n==> KernelNetlinkProtocol sent new data (", ret, " bytes) to socket ", cmd_sock_); static std::vector<std::string> messages; static uint16_t multi_type; int error = 5; bool all = false; int dim = 0; while (!all) { dim = recv(cmd_sock_, cmd_rx_buffer_.data(), cmd_rx_buffer_.size(), 0); // sanity checks if (dim <= 0) { if (dim < -1) { tnt::Log::error("KernelNetlinkProtocol::send_and_parse: recv returned ", dim); } return dim; } //tnt::Log::info(colors::blue, "\n==> received new data from socket ", cmd_sock_, " (command socket)"); std::string raw_input(cmd_rx_buffer_.data(), dim); size_t len = raw_input.size(); size_t pos = 0; for (const nlmsghdr* nlh = reinterpret_cast<const nlmsghdr*>(raw_input.data()); NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { if (netlink_debug) print_nlmsghdr_info(nlh); pos += nlh->nlmsg_len; //tnt::Log::info(raw_input.size() - pos, " of ", raw_input.size()," bytes left"); if (nlh->nlmsg_flags & NLM_F_MULTI) // Multipart message { if (nlh->nlmsg_type == NLMSG_DONE) // Multipart message ended, we can start parsing all the previous messages all together { //tnt::Log::info(colors::green, "\n----> multipart ended, now parsing"); switch (multi_type) { case RTM_NEWLINK: tnt::Application::raise(event::PortList(parse_multi<std::shared_ptr<NetworkPort>>(messages, link_parser)), this); break; case RTM_NEWADDR: tnt::Application::raise(event::AddressList(parse_multi<AddressInfo>(messages, address_parser)), this); break; case RTM_NEWROUTE: tnt::Application::raise(event::RouteList(parse_multi<RouteInfo>(messages, route_parser)), this); break; default: break; } messages.clear(); error = 0; } else { multi_type = nlh->nlmsg_type; messages.push_back(raw_input.substr(pos - nlh->nlmsg_len, pos)); continue; // do not parse yet, thus continue; } } else // single message { //tnt::Log::info(colors::green, "\n----> single message, now parsing"); if (nlh->nlmsg_type == NLMSG_ERROR) { nlmsgerr* nl_err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(nlh)); if (nl_err->error) { tnt::Log::warning("error message, code: ", nl_err->error, "\tin reply to message ", type2string(nl_err->msg.nlmsg_type), ", sequence ", nl_err->msg.nlmsg_seq); } else { //tnt::Log::info("ACK message\tin reply to message ", type2string(nl_err->msg.nlmsg_type), ", sequence ", nl_err->msg.nlmsg_seq); } error = -(nl_err->error); } } all = true; } // sanity checks if (raw_input.size() - pos > 0) { tnt::Log::warning(colors::red, "unable to parse everything (", len, " bytes remaining)"); raw_input = raw_input.substr(pos); } else { raw_input.clear(); } } return error; }
int main(int argc, char **argv) { int opt, longidx, nlsock, rc; struct sockaddr_nl nladdr = {AF_NETLINK}; socklen_t nlalen; pid_t mypid; ssize_t bcount; char *called; void *rcvbuf; /* Isolate the base name of the program as invoked. */ called = strrchr(argv[0], '/'); if (!called) called = argv[0]; /* Parse the given options, looking for the filtering mode. */ while ((opt = getopt_long(argc, argv, my_short_opts, my_long_opts, &longidx)) != -1) { switch (opt) { case 'e': execflag = 1; break; case 'f': forkflag = 1; break; case 't': threadflag = 1; break; default: if (opt != 'h') fprintf(stderr, "%s: Invalid option '%c' !\n", called, opt); usage(called); } } /* If no filtering mode, bail out. */ if (!(execflag || forkflag)) { fprintf(stderr, "%s: Missing required mode option!\n", called); usage(called); } /* Create the netlink socket */ nlsock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_CONNECTOR); if (nlsock == -1) { perror("Unable to open a netlink socket!"); exit(1); } /* Attach to the process connector group */ { nladdr.nl_pid = mypid = getpid(); nladdr.nl_groups = CN_IDX_PROC; if (bind(nlsock, (struct sockaddr *)&nladdr, sizeof(nladdr))) { perror("Unable to bind to the process connector!"); exit(1); } } /* Request the process messages */ { enum proc_cn_mcast_op cnop = PROC_CN_MCAST_LISTEN; struct cn_msg cnmsg = {{CN_IDX_PROC, CN_VAL_PROC}, 0, 0, sizeof(cnop), 0}; struct nlmsghdr nlmsg = {NLMSG_LENGTH(sizeof cnmsg + sizeof cnop), NLMSG_DONE}; char padding[16]; struct iovec iov[4] = { {&nlmsg, sizeof(nlmsg)}, {padding, NLMSG_LENGTH(0) - sizeof(nlmsg)}, {&cnmsg, sizeof(cnmsg)}, {&cnop, sizeof(cnop)} }; nlmsg.nlmsg_pid = mypid; if ((bcount = writev(nlsock, iov, 4)) == -1) { perror("Unable to listen to the process connector!"); exit(1); } } /* Receive messages forever ... */ rcvbuf = malloc(4096+CONNECTOR_MAX_MSG_SIZE); if (!rcvbuf) { perror("Unable to allocate a receive buffer!"); exit(1); } setbuf(stdout, NULL); while (1) { nlalen = sizeof(nladdr); bcount = recvfrom(nlsock, rcvbuf, 4096+CONNECTOR_MAX_MSG_SIZE, 0, (struct sockaddr *)&nladdr, &nlalen); if (nladdr.nl_pid == 0) { struct nlmsghdr *hdr = rcvbuf; for (hdr=rcvbuf; NLMSG_OK(hdr, bcount); hdr=NLMSG_NEXT(hdr, bcount)) dispatch_nl(hdr); } } }
/* ====================================================================== */ int getifaddrs(struct ifaddrs **ifap) { int sd; struct nlmsg_list *nlmsg_list, *nlmsg_end, *nlm; /* - - - - - - - - - - - - - - - */ int icnt; size_t dlen, xlen, nlen; uint32_t max_ifindex = 0; pid_t pid = getpid(); int seq; int result; int build ; /* 0 or 1 */ /* ---------------------------------- */ /* initialize */ icnt = dlen = xlen = nlen = 0; nlmsg_list = nlmsg_end = NULL; if (ifap) *ifap = NULL; /* ---------------------------------- */ /* open socket and bind */ sd = nl_open(); if (sd < 0) return -1; /* ---------------------------------- */ /* gather info */ if ((seq = nl_getlist(sd, 0, RTM_GETLINK, &nlmsg_list, &nlmsg_end)) < 0){ free_nlmsglist(nlmsg_list); nl_close(sd); return -1; } if ((seq = nl_getlist(sd, seq+1, RTM_GETADDR, &nlmsg_list, &nlmsg_end)) < 0){ free_nlmsglist(nlmsg_list); nl_close(sd); return -1; } /* ---------------------------------- */ /* Estimate size of result buffer and fill it */ for (build=0; build<=1; build++){ struct ifaddrs *ifl = NULL, *ifa = NULL; struct nlmsghdr *nlh, *nlh0; void *data = NULL, *xdata = NULL, *ifdata = NULL; char *ifname = NULL, **iflist = NULL; uint16_t *ifflist = NULL; struct rtmaddr_ifamap ifamap; if (build){ ifa = data = calloc(1, NLMSG_ALIGN(sizeof(struct ifaddrs[icnt])) + dlen + xlen + nlen); ifdata = calloc(1, NLMSG_ALIGN(sizeof(char *[max_ifindex+1])) + NLMSG_ALIGN(sizeof(uint16_t [max_ifindex+1]))); if (ifap != NULL) *ifap = (ifdata != NULL) ? ifa : NULL; else{ free_data(data, ifdata); result = 0; break; } if (data == NULL || ifdata == NULL){ free_data(data, ifdata); result = -1; break; } ifl = NULL; data += NLMSG_ALIGN(sizeof(struct ifaddrs)) * icnt; xdata = data + dlen; ifname = xdata + xlen; iflist = ifdata; ifflist = ((void *)iflist) + NLMSG_ALIGN(sizeof(char *[max_ifindex+1])); } for (nlm=nlmsg_list; nlm; nlm=nlm->nlm_next){ int nlmlen = nlm->size; if (!(nlh0 = nlm->nlh)) continue; for (nlh = nlh0; NLMSG_OK(nlh, nlmlen); nlh=NLMSG_NEXT(nlh,nlmlen)){ struct ifinfomsg *ifim = NULL; struct ifaddrmsg *ifam = NULL; struct rtattr *rta; size_t nlm_struct_size = 0; sa_family_t nlm_family = 0; uint32_t nlm_scope = 0, nlm_index = 0; #ifndef IFA_NETMASK size_t sockaddr_size = 0; uint32_t nlm_prefixlen = 0; #endif size_t rtasize; memset(&ifamap, 0, sizeof(ifamap)); /* check if the message is what we want */ if (nlh->nlmsg_pid != pid || nlh->nlmsg_seq != nlm->seq) continue; if (nlh->nlmsg_type == NLMSG_DONE){ break; /* ok */ } switch (nlh->nlmsg_type){ case RTM_NEWLINK: ifim = (struct ifinfomsg *)NLMSG_DATA(nlh); nlm_struct_size = sizeof(*ifim); nlm_family = ifim->ifi_family; nlm_scope = 0; nlm_index = ifim->ifi_index; nlm_prefixlen = 0; if (build) ifflist[nlm_index] = ifa->ifa_flags = ifim->ifi_flags; break; case RTM_NEWADDR: ifam = (struct ifaddrmsg *)NLMSG_DATA(nlh); nlm_struct_size = sizeof(*ifam); nlm_family = ifam->ifa_family; nlm_scope = ifam->ifa_scope; nlm_index = ifam->ifa_index; nlm_prefixlen = ifam->ifa_prefixlen; if (build) ifa->ifa_flags = ifflist[nlm_index]; break; default: continue; } if (!build){ if (max_ifindex < nlm_index) max_ifindex = nlm_index; } else { if (ifl != NULL) ifl->ifa_next = ifa; } rtasize = NLMSG_PAYLOAD(nlh, nlmlen) - NLMSG_ALIGN(nlm_struct_size); for (rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + NLMSG_ALIGN(nlm_struct_size)); RTA_OK(rta, rtasize); rta = RTA_NEXT(rta, rtasize)){ struct sockaddr **sap = NULL; void *rtadata = RTA_DATA(rta); size_t rtapayload = RTA_PAYLOAD(rta); socklen_t sa_len; switch(nlh->nlmsg_type){ case RTM_NEWLINK: switch(rta->rta_type){ case IFLA_ADDRESS: case IFLA_BROADCAST: if (build){ sap = (rta->rta_type == IFLA_ADDRESS) ? &ifa->ifa_addr : &ifa->ifa_broadaddr; *sap = (struct sockaddr *)data; } sa_len = ifa_sa_len(AF_PACKET, rtapayload); if (rta->rta_type == IFLA_ADDRESS) sockaddr_size = NLMSG_ALIGN(sa_len); if (!build){ dlen += NLMSG_ALIGN(sa_len); } else { memset(*sap, 0, sa_len); ifa_make_sockaddr(AF_PACKET, *sap, rtadata,rtapayload, 0,0); ((struct sockaddr_ll *)*sap)->sll_ifindex = nlm_index; ((struct sockaddr_ll *)*sap)->sll_hatype = ifim->ifi_type; data += NLMSG_ALIGN(sa_len); } break; case IFLA_IFNAME:/* Name of Interface */ if (!build) nlen += NLMSG_ALIGN(rtapayload + 1); else{ ifa->ifa_name = ifname; if (iflist[nlm_index] == NULL) iflist[nlm_index] = ifa->ifa_name; strncpy(ifa->ifa_name, rtadata, rtapayload); ifa->ifa_name[rtapayload] = '\0'; ifname += NLMSG_ALIGN(rtapayload + 1); } break; case IFLA_STATS:/* Statistics of Interface */ if (!build) xlen += NLMSG_ALIGN(rtapayload); else{ ifa->ifa_data = xdata; memcpy(ifa->ifa_data, rtadata, rtapayload); xdata += NLMSG_ALIGN(rtapayload); } break; case IFLA_UNSPEC: break; case IFLA_MTU: break; case IFLA_LINK: break; case IFLA_QDISC: break; } break; case RTM_NEWADDR: if (nlm_family == AF_PACKET) break; switch(rta->rta_type){ case IFA_ADDRESS: ifamap.address = rtadata; ifamap.address_len = rtapayload; break; case IFA_LOCAL: ifamap.local = rtadata; ifamap.local_len = rtapayload; break; case IFA_BROADCAST: ifamap.broadcast = rtadata; ifamap.broadcast_len = rtapayload; break; #ifdef HAVE_IFADDRS_IFA_ANYCAST case IFA_ANYCAST: ifamap.anycast = rtadata; ifamap.anycast_len = rtapayload; break; #endif case IFA_LABEL: if (!build) nlen += NLMSG_ALIGN(rtapayload + 1); else{ ifa->ifa_name = ifname; if (iflist[nlm_index] == NULL) iflist[nlm_index] = ifname; strncpy(ifa->ifa_name, rtadata, rtapayload); ifa->ifa_name[rtapayload] = '\0'; ifname += NLMSG_ALIGN(rtapayload + 1); } break; case IFA_UNSPEC: break; case IFA_CACHEINFO: break; } } } if (nlh->nlmsg_type == RTM_NEWADDR && nlm_family != AF_PACKET) { if (!ifamap.local) { ifamap.local = ifamap.address; ifamap.local_len = ifamap.address_len; } if (!ifamap.address) { ifamap.address = ifamap.local; ifamap.address_len = ifamap.local_len; } if (ifamap.address_len != ifamap.local_len || (ifamap.address != NULL && memcmp(ifamap.address, ifamap.local, ifamap.address_len))) { /* p2p; address is peer and local is ours */ ifamap.broadcast = ifamap.address; ifamap.broadcast_len = ifamap.address_len; ifamap.address = ifamap.local; ifamap.address_len = ifamap.local_len; } if (ifamap.address) { #ifndef IFA_NETMASK sockaddr_size = NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len)); #endif if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.address_len)); else { ifa->ifa_addr = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_addr, ifamap.address, ifamap.address_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.address_len)); } } #ifdef IFA_NETMASK if (ifamap.netmask) { if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.netmask_len)); else { ifa->ifa_netmask = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_netmask, ifamap.netmask, ifamap.netmask_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.netmask_len)); } } #endif if (ifamap.broadcast) { if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.broadcast_len)); else { ifa->ifa_broadaddr = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_broadaddr, ifamap.broadcast, ifamap.broadcast_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.broadcast_len)); } } #ifdef HAVE_IFADDRS_IFA_ANYCAST if (ifamap.anycast) { if (!build) dlen += NLMSG_ALIGN(ifa_sa_len(nlm_family,ifamap.anycast_len)); else { ifa->ifa_anycast = (struct sockaddr *)data; ifa_make_sockaddr(nlm_family, ifa->ifa_anyaddr, ifamap.anycast, ifamap.anycast_len, nlm_scope, nlm_index); data += NLMSG_ALIGN(ifa_sa_len(nlm_family, ifamap.anycast_len)); } } #endif } if (!build){ #ifndef IFA_NETMASK dlen += sockaddr_size; #endif icnt++; } else { if (ifa->ifa_name == NULL) ifa->ifa_name = iflist[nlm_index]; #ifndef IFA_NETMASK if (ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_UNSPEC && ifa->ifa_addr->sa_family != AF_PACKET){ ifa->ifa_netmask = (struct sockaddr *)data; ifa_make_sockaddr_mask(ifa->ifa_addr->sa_family, ifa->ifa_netmask, nlm_prefixlen); } data += sockaddr_size; #endif ifl = ifa++; } } } if (!build){ if (icnt == 0 && (dlen + nlen + xlen == 0)){ if (ifap != NULL) *ifap = NULL; break; /* cannot found any addresses */ } } else free_data(NULL, ifdata); } /* ---------------------------------- */ /* Finalize */ free_nlmsglist(nlmsg_list); nl_close(sd); return 0; }
std::vector<ip_route> enum_routes(io_service& ios, error_code& ec) { std::vector<ip_route> ret; #if TORRENT_USE_SYSCTL /* struct rt_msg { rt_msghdr m_rtm; char buf[512]; }; rt_msg m; int len = sizeof(rt_msg); bzero(&m, len); m.m_rtm.rtm_type = RTM_GET; m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY; m.m_rtm.rtm_version = RTM_VERSION; m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; m.m_rtm.rtm_seq = 0; m.m_rtm.rtm_msglen = len; int s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); if (s == -1) { ec = error_code(errno, asio::error::system_category); return std::vector<ip_route>(); } int n = write(s, &m, len); if (n == -1) { ec = error_code(errno, asio::error::system_category); close(s); return std::vector<ip_route>(); } else if (n != len) { ec = asio::error::operation_not_supported; close(s); return std::vector<ip_route>(); } bzero(&m, len); n = read(s, &m, len); if (n == -1) { ec = error_code(errno, asio::error::system_category); close(s); return std::vector<ip_route>(); } for (rt_msghdr* ptr = &m.m_rtm; (char*)ptr < ((char*)&m.m_rtm) + n; ptr = (rt_msghdr*)(((char*)ptr) + ptr->rtm_msglen)) { std::cout << " rtm_msglen: " << ptr->rtm_msglen << std::endl; std::cout << " rtm_type: " << ptr->rtm_type << std::endl; if (ptr->rtm_errno) { ec = error_code(ptr->rtm_errno, asio::error::system_category); return std::vector<ip_route>(); } if (m.m_rtm.rtm_flags & RTF_UP == 0 || m.m_rtm.rtm_flags & RTF_GATEWAY == 0) { ec = asio::error::operation_not_supported; return address_v4::any(); } if (ptr->rtm_addrs & RTA_DST == 0 || ptr->rtm_addrs & RTA_GATEWAY == 0 || ptr->rtm_addrs & RTA_NETMASK == 0) { ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } if (ptr->rtm_msglen > len - ((char*)ptr - ((char*)&m.m_rtm))) { ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } int min_len = sizeof(rt_msghdr) + 2 * sizeof(sockaddr_in); if (m.m_rtm.rtm_msglen < min_len) { ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } ip_route r; // destination char* p = m.buf; sockaddr_in* sin = (sockaddr_in*)p; r.destination = sockaddr_to_address((sockaddr*)p); // gateway p += sin->sin_len; sin = (sockaddr_in*)p; r.gateway = sockaddr_to_address((sockaddr*)p); // netmask p += sin->sin_len; sin = (sockaddr_in*)p; r.netmask = sockaddr_to_address((sockaddr*)p); ret.push_back(r); } close(s); */ int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_DUMP, 0}; size_t needed = 0; #ifdef TORRENT_OS2 if (__libsocket_sysctl(mib, 6, 0, &needed, 0, 0) < 0) #else if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) #endif { ec = error_code(errno, asio::error::system_category); return std::vector<ip_route>(); } if (needed <= 0) { return std::vector<ip_route>(); } boost::scoped_array<char> buf(new (std::nothrow) char[needed]); if (buf.get() == 0) { ec = asio::error::no_memory; return std::vector<ip_route>(); } #ifdef TORRENT_OS2 if (__libsocket_sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) #else if (sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) #endif { ec = error_code(errno, asio::error::system_category); return std::vector<ip_route>(); } char* end = buf.get() + needed; int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { ec = error_code(errno, asio::error::system_category); return std::vector<ip_route>(); } rt_msghdr* rtm; for (char* next = buf.get(); next < end; next += rtm->rtm_msglen) { rtm = (rt_msghdr*)next; if (rtm->rtm_version != RTM_VERSION) continue; ip_route r; if (parse_route(s, rtm, &r)) ret.push_back(r); } close(s); #elif TORRENT_USE_GETIPFORWARDTABLE /* move this to enum_net_interfaces // Load Iphlpapi library HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); if (!iphlp) { ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } // Get GetAdaptersInfo() pointer typedef DWORD (WINAPI *GetAdaptersInfo_t)(PIP_ADAPTER_INFO, PULONG); GetAdaptersInfo_t GetAdaptersInfo = (GetAdaptersInfo_t)GetProcAddress(iphlp, "GetAdaptersInfo"); if (!GetAdaptersInfo) { FreeLibrary(iphlp); ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } PIP_ADAPTER_INFO adapter_info = 0; ULONG out_buf_size = 0; if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW) { FreeLibrary(iphlp); ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } adapter_info = (IP_ADAPTER_INFO*)malloc(out_buf_size); if (!adapter_info) { FreeLibrary(iphlp); ec = asio::error::no_memory; return std::vector<ip_route>(); } if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR) { for (PIP_ADAPTER_INFO adapter = adapter_info; adapter != 0; adapter = adapter->Next) { ip_route r; r.destination = address::from_string(adapter->IpAddressList.IpAddress.String, ec); r.gateway = address::from_string(adapter->GatewayList.IpAddress.String, ec); r.netmask = address::from_string(adapter->IpAddressList.IpMask.String, ec); strncpy(r.name, adapter->AdapterName, sizeof(r.name)); if (ec) { ec = error_code(); continue; } ret.push_back(r); } } // Free memory free(adapter_info); FreeLibrary(iphlp); */ // Load Iphlpapi library HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); if (!iphlp) { ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } typedef DWORD (WINAPI *GetIfEntry_t)(PMIB_IFROW pIfRow); GetIfEntry_t GetIfEntry = (GetIfEntry_t)GetProcAddress(iphlp, "GetIfEntry"); if (!GetIfEntry) { ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } #if _WIN32_WINNT >= 0x0600 typedef DWORD (WINAPI *GetIpForwardTable2_t)( ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2*); typedef void (WINAPI *FreeMibTable_t)(PVOID Memory); GetIpForwardTable2_t GetIpForwardTable2 = (GetIpForwardTable2_t)GetProcAddress( iphlp, "GetIpForwardTable2"); FreeMibTable_t FreeMibTable = (FreeMibTable_t)GetProcAddress( iphlp, "FreeMibTable"); if (GetIpForwardTable2 && FreeMibTable) { MIB_IPFORWARD_TABLE2* routes = NULL; int res = GetIpForwardTable2(AF_UNSPEC, &routes); if (res == NO_ERROR) { for (int i = 0; i < routes->NumEntries; ++i) { ip_route r; r.gateway = sockaddr_to_address((const sockaddr*)&routes->Table[i].NextHop); r.destination = sockaddr_to_address( (const sockaddr*)&routes->Table[i].DestinationPrefix.Prefix); r.netmask = build_netmask(routes->Table[i].SitePrefixLength , routes->Table[i].DestinationPrefix.Prefix.si_family); MIB_IFROW ifentry; ifentry.dwIndex = routes->Table[i].InterfaceIndex; if (GetIfEntry(&ifentry) == NO_ERROR) { wcstombs(r.name, ifentry.wszName, sizeof(r.name)); r.mtu = ifentry.dwMtu; ret.push_back(r); } } } if (routes) FreeMibTable(routes); FreeLibrary(iphlp); return ret; } #endif // Get GetIpForwardTable() pointer typedef DWORD (WINAPI *GetIpForwardTable_t)(PMIB_IPFORWARDTABLE pIpForwardTable,PULONG pdwSize,BOOL bOrder); GetIpForwardTable_t GetIpForwardTable = (GetIpForwardTable_t)GetProcAddress( iphlp, "GetIpForwardTable"); if (!GetIpForwardTable) { FreeLibrary(iphlp); ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } MIB_IPFORWARDTABLE* routes = NULL; ULONG out_buf_size = 0; if (GetIpForwardTable(routes, &out_buf_size, FALSE) != ERROR_INSUFFICIENT_BUFFER) { FreeLibrary(iphlp); ec = asio::error::operation_not_supported; return std::vector<ip_route>(); } routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size); if (!routes) { FreeLibrary(iphlp); ec = asio::error::no_memory; return std::vector<ip_route>(); } if (GetIpForwardTable(routes, &out_buf_size, FALSE) == NO_ERROR) { for (int i = 0; i < routes->dwNumEntries; ++i) { ip_route r; r.destination = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardDest); r.netmask = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardMask); r.gateway = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardNextHop); MIB_IFROW ifentry; ifentry.dwIndex = routes->table[i].dwForwardIfIndex; if (GetIfEntry(&ifentry) == NO_ERROR) { wcstombs(r.name, ifentry.wszName, sizeof(r.name)); r.name[sizeof(r.name)-1] = 0; r.mtu = ifentry.dwMtu; ret.push_back(r); } } } // Free memory free(routes); FreeLibrary(iphlp); #elif TORRENT_USE_NETLINK enum { BUFSIZE = 8192 }; int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); if (sock < 0) { ec = error_code(errno, asio::error::system_category); return std::vector<ip_route>(); } int seq = 0; char msg[BUFSIZE]; memset(msg, 0, BUFSIZE); nlmsghdr* nl_msg = (nlmsghdr*)msg; nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); nl_msg->nlmsg_type = RTM_GETROUTE; nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nl_msg->nlmsg_seq = seq++; nl_msg->nlmsg_pid = getpid(); if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) { ec = error_code(errno, asio::error::system_category); close(sock); return std::vector<ip_route>(); } int len = read_nl_sock(sock, msg, BUFSIZE, seq, getpid()); if (len < 0) { ec = error_code(errno, asio::error::system_category); close(sock); return std::vector<ip_route>(); } int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { ec = error_code(errno, asio::error::system_category); return std::vector<ip_route>(); } for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) { ip_route r; if (parse_route(s, nl_msg, &r)) ret.push_back(r); } close(s); close(sock); #endif return ret; }
void Netlink::processNetlinkTelegram(void *pReadBuffer, size_t bufferSize) const { for (struct nlmsghdr *nh = reinterpret_cast <struct nlmsghdr *> (pReadBuffer); NLMSG_OK (nh, bufferSize); nh = NLMSG_NEXT (nh, bufferSize)) { if (nh->nlmsg_type == NLMSG_DONE) { // The end of multipart message. break; } else if (nh->nlmsg_type == NLMSG_ERROR) { ::syslog(LOG_ERR, "error processing netlink events"); break; } else { m_netadapterlist.update(); switch(nh->nlmsg_type) { case RTM_NEWADDR: { struct ifaddrmsg* pIfaddrmsg = reinterpret_cast <struct ifaddrmsg*> (NLMSG_DATA(nh)); if(pIfaddrmsg->ifa_family==AF_INET) { struct rtattr *rth = IFA_RTA(pIfaddrmsg); int rtl = IFA_PAYLOAD(nh); while (rtl && RTA_OK(rth, rtl)) { if (rth->rta_type == IFA_LOCAL) { // this is to be ignored if there are more than one ipv4 addresses assigned to the interface! try { communication::Netadapter adapter = m_netadapterlist.getAdapterByInterfaceIndex(pIfaddrmsg->ifa_index); if(adapter.getIpv4Addresses().size()==1) { if (m_eventHandler) { struct in_addr* pIn = reinterpret_cast < struct in_addr* > (RTA_DATA(rth)); m_eventHandler(NEW, pIfaddrmsg->ifa_index, inet_ntoa(*pIn)); } } } catch(const hbm::exception::exception&) { } } rth = RTA_NEXT(rth, rtl); } } } break; case RTM_DELADDR: { struct ifaddrmsg* pIfaddrmsg = reinterpret_cast <struct ifaddrmsg*> (NLMSG_DATA(nh)); if(pIfaddrmsg->ifa_family==AF_INET) { struct rtattr *rth = IFA_RTA(pIfaddrmsg); int rtl = IFA_PAYLOAD(nh); while (rtl && RTA_OK(rth, rtl)) { if (rth->rta_type == IFA_LOCAL) { // this is to be ignored if there is another ipv4 address left for the interface! try { communication::Netadapter adapter = m_netadapterlist.getAdapterByInterfaceIndex(pIfaddrmsg->ifa_index); if(adapter.getIpv4Addresses().empty()==true) { if (m_eventHandler) { struct in_addr* pIn = reinterpret_cast < struct in_addr* > (RTA_DATA(rth)); m_eventHandler(NEW, pIfaddrmsg->ifa_index, inet_ntoa(*pIn)); } } } catch(const hbm::exception::exception&) { } } rth = RTA_NEXT(rth, rtl); } } } break; default: break; } } } }
/* * See if left->addr or left->next is %defaultroute and change it to IP. * * Returns: * -1: failure * 0: done * 1: please call again: more to do */ static int resolve_defaultroute_one(struct starter_end *host, struct starter_end *peer) { /* * "left=" == host->addrtype and host->addr * "leftnexthop=" == host->nexttype and host->nexthop */ /* What kind of result are we seeking? */ bool seeking_src = (host->addrtype == KH_DEFAULTROUTE); bool seeking_gateway = (host->nexttype == KH_DEFAULTROUTE); char msgbuf[RTNL_BUFSIZE]; bool has_dst = FALSE; int query_again = 0; if (!seeking_src && !seeking_gateway) return 0; /* this end already figured out */ /* Fill netlink request */ netlink_query_init(msgbuf, host->addr_family); if (host->nexttype == KH_IPADDR) { /* * My nexthop (gateway) is specified. * We need to figure out our source IP to get there. */ netlink_query_add(msgbuf, RTA_DST, &host->nexthop); has_dst = TRUE; } else if (peer->addrtype == KH_IPADDR) { /* * Peer IP is specified. * We may need to figure out source IP * and gateway IP to get there. */ netlink_query_add(msgbuf, RTA_DST, &peer->addr); has_dst = TRUE; if (seeking_src && seeking_gateway && host->addr_family == AF_INET) { /* * If we have only peer IP and no gateway/src we must * do two queries: * 1) find out gateway for dst * 2) find out src for that gateway * Doing both in one query returns src for dst. * * (IPv6 returns link-local for gateway so we can and * do seek both in one query.) */ seeking_src = FALSE; query_again = 1; } } if (has_dst && host->addrtype == KH_IPADDR) { /* SRC works only with DST */ netlink_query_add(msgbuf, RTA_SRC, &host->addr); } /* * If we have for example host=%defaultroute + peer=%any * (no destination) the netlink reply will be full routing table. * We must do two queries: * 1) find out default gateway * 2) find out src for that default gateway */ if (!has_dst) { if (seeking_src && seeking_gateway) { seeking_src = FALSE; query_again = 1; } } if (seeking_gateway) { struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf; nlmsg->nlmsg_flags |= NLM_F_DUMP; } if (verbose) printf("\nseeking_src = %d, seeking_gateway = %d, has_dst = %d\n", seeking_src, seeking_gateway, has_dst); /* Send netlink get_route request */ ssize_t len = netlink_query(msgbuf); if (len < 0) return -1; /* Parse reply */ struct nlmsghdr *nlmsg = (struct nlmsghdr *)msgbuf; for (; NLMSG_OK(nlmsg, len); nlmsg = NLMSG_NEXT(nlmsg, len)) { struct rtmsg *rtmsg; struct rtattr *rtattr; int rtlen; char r_interface[IF_NAMESIZE+1]; char r_source[ADDRTOT_BUF]; char r_gateway[ADDRTOT_BUF]; char r_destination[ADDRTOT_BUF]; bool ignore; if (nlmsg->nlmsg_type == NLMSG_DONE) break; if (nlmsg->nlmsg_type == NLMSG_ERROR) { printf("netlink error\n"); return -1; break; } /* ignore all but IPv4 and IPv6 */ rtmsg = (struct rtmsg *) NLMSG_DATA(nlmsg); if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6) continue; /* Parse one route entry */ r_interface[0] = r_interface[IF_NAMESIZE] = r_source[0] = r_gateway[0] = r_destination[0] = '\0'; rtattr = (struct rtattr *) RTM_RTA(rtmsg); rtlen = RTM_PAYLOAD(nlmsg); for (; RTA_OK(rtattr, rtlen); rtattr = RTA_NEXT(rtattr, rtlen)) { switch (rtattr->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(rtattr), r_interface); break; case RTA_PREFSRC: inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr), r_source, sizeof(r_source)); break; case RTA_GATEWAY: inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr), r_gateway, sizeof(r_gateway)); break; case RTA_DST: inet_ntop(rtmsg->rtm_family, RTA_DATA(rtattr), r_destination, sizeof(r_destination)); break; } } /* * Ignore if not main table. * Ignore ipsecX or mastX interfaces. */ ignore = rtmsg->rtm_table != RT_TABLE_MAIN || startswith(r_interface, "ipsec") || startswith(r_interface, "mast"); if (verbose) { printf("dst %s via %s dev %s src %s table %d%s\n", r_destination, r_gateway, r_interface, r_source, rtmsg->rtm_table, ignore ? "" : " (ignored)"); } if (ignore) continue; if (seeking_src && r_source[0] != '\0') { err_t err = tnatoaddr(r_source, 0, rtmsg->rtm_family, &host->addr); if (err == NULL) { host->addrtype = KH_IPADDR; seeking_src = FALSE; if (verbose) printf("set addr: %s\n", r_source); } else if (verbose) { printf("unknown source results from kernel (%s): %s\n", r_source, err); } } if (seeking_gateway && r_destination[0] == '\0' && (has_dst || r_source[0] == '\0')) { if (r_gateway[0] == '\0' && r_interface[0] != '\0') { /* * Point-to-Point default gw without "via IP" * Attempt to find r_gateway as the IP address * on the interface. */ resolve_ppp_peer(r_interface, host->addr_family, r_gateway); } if (r_gateway[0] != '\0') { err_t err = tnatoaddr(r_gateway, 0, rtmsg->rtm_family, &host->nexthop); if (err != NULL) { printf("unknown gateway results from kernel: %s\n", err); } else { /* Note: Use first even if multiple */ host->nexttype = KH_IPADDR; seeking_gateway = FALSE; if (verbose) printf("set nexthop: %s\n", r_gateway); } } } } return query_again; }
/* * parse messages in received notifications from kernel */ int parse_events(struct msghdr *mhdr) { struct nlmsghdr *nlh; struct nlmsgerr *nle; int nlh_len; /* get netlink message header */ nlh = mhdr->msg_iov->iov_base; nlh_len = mhdr->msg_iov->iov_len; /* parse netlink message type */ for( ; NLMSG_OK(nlh, nlh_len); nlh = NLMSG_NEXT(nlh, nlh_len)) { switch(nlh->nlmsg_type) { /* interface link message */ case RTM_NEWLINK: case RTM_DELLINK: parse_ifimsg(nlh); break; /* interface address message */ case RTM_NEWADDR: case RTM_DELADDR: parse_ifamsg(nlh); break; /* neighbor discovery message */ case RTM_NEWNEIGH: case RTM_DELNEIGH: parse_ndmsg(nlh); break; /* route message */ case RTM_NEWROUTE: case RTM_DELROUTE: parse_rtmsg(nlh); break; #ifdef HAVE_LINUX_FIB_RULES_H /* fib rule header */ case RTM_NEWRULE: case RTM_DELRULE: parse_frhdr(nlh); break; #endif /* traffic control header */ case RTM_NEWQDISC: case RTM_DELQDISC: case RTM_NEWTCLASS: case RTM_DELTCLASS: parse_tcmsg_qdisc(nlh); break; case RTM_NEWTFILTER: case RTM_DELTFILTER: parse_tcmsg_filter(nlh); break; case RTM_NEWACTION: case RTM_DELACTION: parse_tcamsg(nlh); break; /* error message */ case NLMSG_ERROR: nle = (struct nlmsgerr *)NLMSG_DATA(nlh); rec_log("error: %s: nlmsg error: %s", __func__, strerror(nle->error)); break; /* unknown message */ default: rec_log("error: %s: unknown nlsmg_type: %d", __func__, (int)nlh->nlmsg_type); } } return(0); }
static int rtnl_dump_filter(struct rtnl_handle *rth, int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *n, void *), void *arg1/*, int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *arg2*/) { int retval = -1; char *buf = xmalloc(8*1024); /* avoid big stack buffer */ struct sockaddr_nl nladdr; struct iovec iov = { buf, 8*1024 }; while (1) { int status; struct nlmsghdr *h; struct msghdr msg = { (void*)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; status = recvmsg(rth->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; bb_perror_msg("OVERRUN"); continue; } if (status == 0) { bb_error_msg("EOF on netlink"); goto ret; } if (msg.msg_namelen != sizeof(nladdr)) { bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); } h = (struct nlmsghdr*)buf; while (NLMSG_OK(h, status)) { int err; if (nladdr.nl_pid != 0 || h->nlmsg_pid != rth->local.nl_pid || h->nlmsg_seq != rth->dump) { // if (junk) { // err = junk(&nladdr, h, arg2); // if (err < 0) { // retval = err; // goto ret; // } // } goto skip_it; } if (h->nlmsg_type == NLMSG_DONE) { goto ret_0; } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h); if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { bb_error_msg("ERROR truncated"); } else { errno = -l_err->error; bb_perror_msg("RTNETLINK answers"); } goto ret; } err = filter(&nladdr, h, arg1); if (err < 0) { retval = err; goto ret; } skip_it: h = NLMSG_NEXT(h, status); } if (msg.msg_flags & MSG_TRUNC) { bb_error_msg("message truncated"); continue; } if (status) { bb_error_msg_and_die("remnant of size %d!", status); } } /* while (1) */ ret_0: retval++; /* = 0 */ ret: free(buf); return retval; }
int main(int argc, char *argv[]) { int c, rc, rep_len, aggr_len, len2; int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC; __u16 id; __u32 mypid; struct nlattr *na; int nl_sd = -1; int len = 0; pid_t tid = 0; pid_t rtid = 0; int fd = 0; int count = 0; int write_file = 0; int maskset = 0; char *logfile = NULL; int loop = 0; int containerset = 0; char *containerpath = NULL; int cfd = 0; int forking = 0; sigset_t sigset; struct msgtemplate msg; while (!forking) { c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:"); if (c < 0) break; switch (c) { case 'd': printf("print delayacct stats ON\n"); print_delays = 1; break; case 'i': printf("printing IO accounting\n"); print_io_accounting = 1; break; case 'q': printf("printing task/process context switch rates\n"); print_task_context_switch_counts = 1; break; case 'C': containerset = 1; containerpath = optarg; break; case 'w': logfile = strdup(optarg); printf("write to file %s\n", logfile); write_file = 1; break; case 'r': rcvbufsz = atoi(optarg); printf("receive buf size %d\n", rcvbufsz); if (rcvbufsz < 0) err(1, "Invalid rcv buf size\n"); break; case 'm': strncpy(cpumask, optarg, sizeof(cpumask)); cpumask[sizeof(cpumask) - 1] = '\0'; maskset = 1; printf("cpumask %s maskset %d\n", cpumask, maskset); break; case 't': tid = atoi(optarg); if (!tid) err(1, "Invalid tgid\n"); cmd_type = TASKSTATS_CMD_ATTR_TGID; break; case 'p': tid = atoi(optarg); if (!tid) err(1, "Invalid pid\n"); cmd_type = TASKSTATS_CMD_ATTR_PID; break; case 'c': /* Block SIGCHLD for sigwait() later */ if (sigemptyset(&sigset) == -1) err(1, "Failed to empty sigset"); if (sigaddset(&sigset, SIGCHLD)) err(1, "Failed to set sigchld in sigset"); sigprocmask(SIG_BLOCK, &sigset, NULL); /* fork/exec a child */ tid = fork(); if (tid < 0) err(1, "Fork failed\n"); if (tid == 0) if (execvp(argv[optind - 1], &argv[optind - 1]) < 0) exit(-1); /* Set the command type and avoid further processing */ cmd_type = TASKSTATS_CMD_ATTR_PID; forking = 1; break; case 'v': printf("debug on\n"); dbg = 1; break; case 'l': printf("listen forever\n"); loop = 1; break; default: usage(); exit(-1); } } if (write_file) { fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd == -1) { perror("Cannot open output file\n"); exit(1); } } nl_sd = create_nl_socket(NETLINK_GENERIC); if (nl_sd < 0) err(1, "error creating Netlink socket\n"); mypid = getpid(); id = get_family_id(nl_sd); if (!id) { fprintf(stderr, "Error getting family id, errno %d\n", errno); goto err; } PRINTF("family id %d\n", id); if (maskset) { rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, &cpumask, strlen(cpumask) + 1); PRINTF("Sent register cpumask, retval %d\n", rc); if (rc < 0) { fprintf(stderr, "error sending register cpumask\n"); goto err; } } if (tid && containerset) { fprintf(stderr, "Select either -t or -C, not both\n"); goto err; } /* * If we forked a child, wait for it to exit. Cannot use waitpid() * as all the delicious data would be reaped as part of the wait */ if (tid && forking) { int sig_received; sigwait(&sigset, &sig_received); } if (tid) { rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, cmd_type, &tid, sizeof(__u32)); PRINTF("Sent pid/tgid, retval %d\n", rc); if (rc < 0) { fprintf(stderr, "error sending tid/tgid cmd\n"); goto done; } } if (containerset) { cfd = open(containerpath, O_RDONLY); if (cfd < 0) { perror("error opening container file"); goto err; } rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET, CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32)); if (rc < 0) { perror("error sending cgroupstats command"); goto err; } } if (!maskset && !tid && !containerset) { usage(); goto err; } do { rep_len = recv(nl_sd, &msg, sizeof(msg), 0); PRINTF("received %d bytes\n", rep_len); if (rep_len < 0) { fprintf(stderr, "nonfatal reply error: errno %d\n", errno); continue; } if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&msg.n), rep_len)) { struct nlmsgerr *err = NLMSG_DATA(&msg); fprintf(stderr, "fatal reply error, errno %d\n", err->error); goto done; } PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n", sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); rep_len = GENLMSG_PAYLOAD(&msg.n); na = (struct nlattr *) GENLMSG_DATA(&msg); len = 0; while (len < rep_len) { len += NLA_ALIGN(na->nla_len); switch (na->nla_type) { case TASKSTATS_TYPE_AGGR_TGID: /* Fall through */ case TASKSTATS_TYPE_AGGR_PID: aggr_len = NLA_PAYLOAD(na->nla_len); len2 = 0; /* For nested attributes, na follows */ na = (struct nlattr *) NLA_DATA(na); done = 0; while (len2 < aggr_len) { switch (na->nla_type) { case TASKSTATS_TYPE_PID: rtid = *(int *) NLA_DATA(na); if (print_delays) printf("PID\t%d\n", rtid); break; case TASKSTATS_TYPE_TGID: rtid = *(int *) NLA_DATA(na); if (print_delays) printf("TGID\t%d\n", rtid); break; case TASKSTATS_TYPE_STATS: count++; if (print_delays) print_delayacct((struct taskstats *) NLA_DATA(na)); if (print_io_accounting) print_ioacct((struct taskstats *) NLA_DATA(na)); if (print_task_context_switch_counts) task_context_switch_counts((struct taskstats *) NLA_DATA(na)); if (fd) { if (write(fd, NLA_DATA(na), na->nla_len) < 0) { err(1,"write error\n"); } } if (!loop) goto done; break; case TASKSTATS_TYPE_NULL: break; default: fprintf(stderr, "Unknown nested" " nla_type %d\n", na->nla_type); break; } len2 += NLA_ALIGN(na->nla_len); na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len)); } break; case CGROUPSTATS_TYPE_CGROUP_STATS: print_cgroupstats(NLA_DATA(na)); break; default: fprintf(stderr, "Unknown nla_type %d\n", na->nla_type); case TASKSTATS_TYPE_NULL: break; } na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); } } while (loop); done: if (maskset) { rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, &cpumask, strlen(cpumask) + 1); printf("Sent deregister mask, retval %d\n", rc); if (rc < 0) err(rc, "error sending deregister cpumask\n"); } err: close(nl_sd); if (fd) close(fd); if (cfd) close(cfd); return 0; }
// Source-compatible with the BSD function. int getifaddrs(ifaddrs** result) { // Simplify cleanup for callers. *result = NULL; // Create a netlink socket. ScopedFd fd(socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)); if (fd.get() < 0) { return -1; } // Ask for the address information. addrReq_struct addrRequest; memset(&addrRequest, 0, sizeof(addrRequest)); addrRequest.netlinkHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH; addrRequest.netlinkHeader.nlmsg_type = RTM_GETADDR; addrRequest.netlinkHeader.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(addrRequest))); addrRequest.msg.ifa_family = AF_UNSPEC; // All families. addrRequest.msg.ifa_index = 0; // All interfaces. if (!sendNetlinkMessage(fd.get(), &addrRequest, addrRequest.netlinkHeader.nlmsg_len)) { return -1; } // Read the responses. LocalArray<0> buf(65536); // We don't necessarily have std::vector. ssize_t bytesRead; while ((bytesRead = recvNetlinkMessage(fd.get(), &buf[0], buf.size())) > 0) { nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(&buf[0]); for (; NLMSG_OK(hdr, (unsigned int)bytesRead); hdr = NLMSG_NEXT(hdr, bytesRead)) { switch (hdr->nlmsg_type) { case NLMSG_DONE: { return 0; } case NLMSG_ERROR: { return -1; } case RTM_NEWADDR: { ifaddrmsg* address = reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(hdr)); rtattr* rta = IFA_RTA(address); size_t ifaPayloadLength = IFA_PAYLOAD(hdr); while (RTA_OK(rta, ifaPayloadLength)) { if (rta->rta_type == IFA_LOCAL) { unsigned char family = address->ifa_family; if (family == AF_INET || family == AF_INET6) { *result = new ifaddrs(*result); if (!(*result)->setNameAndFlagsByIndex(address->ifa_index)) { return -1; } (*result)->setAddress(family, RTA_DATA(rta), RTA_PAYLOAD(rta)); (*result)->setNetmask(family, address->ifa_prefixlen); } } rta = RTA_NEXT(rta, ifaPayloadLength); } } break; } } } // We only get here if recv fails before we see a NLMSG_DONE. return -1; }
static int nl_getmsg (int sd, int request, int seq, pid_t pid, struct nlmsghdr **nlhp, int *done) { struct nlmsghdr *nh; size_t bufsize = 65536, lastbufsize = 0; void *buff = NULL; int result = 0, read_size; int msg_flags; for (;;) { void *newbuff = realloc (buff, bufsize); if (newbuff == NULL || bufsize < lastbufsize) { result = -1; break; } buff = newbuff; result = read_size = nl_recvmsg (sd, request, seq, buff, bufsize, &msg_flags); if (read_size < 0 || (msg_flags & MSG_TRUNC)) { lastbufsize = bufsize; bufsize *= 2; continue; } if (read_size == 0) break; nh = (struct nlmsghdr *) buff; for (nh = (struct nlmsghdr *) buff; NLMSG_OK (nh, read_size); nh = (struct nlmsghdr *) NLMSG_NEXT (nh, read_size)) { if (nh->nlmsg_pid != pid || nh->nlmsg_seq != seq) continue; if (nh->nlmsg_type == NLMSG_DONE) { (*done)++; break; /* ok */ } if (nh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nh); result = -1; if (nh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) __set_errno (EIO); else __set_errno (-nlerr->error); break; } } break; } if (result < 0) if (buff) { int saved_errno = errno; free (buff); __set_errno (saved_errno); } *nlhp = (struct nlmsghdr *) buff; return result; }
static int genetlink_call(__u16 family_id, __u8 cmd, void *header, size_t header_len, void *request, size_t request_len, void *reply, size_t reply_len) { struct msg { struct nlmsghdr n; struct genlmsghdr g; char payload[0]; }; struct msg *request_msg; struct msg *reply_msg; int request_msg_size; int reply_msg_size; struct sockaddr_nl local; struct pollfd pfd; int sndbuf = 32*1024; /* 32k */ int rcvbuf = 32*1024; /* 32k */ int len; int sk; /* * Prepare request/reply messages */ request_msg_size = NLMSG_LENGTH(GENL_HDRLEN + header_len + request_len); request_msg = malloc(request_msg_size); request_msg->n.nlmsg_len = request_msg_size; request_msg->n.nlmsg_type = family_id; request_msg->n.nlmsg_flags = NLM_F_REQUEST; request_msg->n.nlmsg_seq = 0; request_msg->n.nlmsg_pid = getpid(); request_msg->g.cmd = cmd; request_msg->g.version = 0; if (header_len) memcpy(&request_msg->payload[0], header, header_len); if (request_len) memcpy(&request_msg->payload[header_len], request, request_len); reply_msg_size = NLMSG_LENGTH(GENL_HDRLEN + header_len + reply_len); reply_msg = malloc(reply_msg_size); /* * Create socket */ memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; if ((sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC)) == -1) fatal("error creating Netlink socket\n"); if ((bind(sk, (struct sockaddr*)&local, sizeof(local)) == -1) || (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) == -1) || (setsockopt(sk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) == -1)) { fatal("error creating Netlink socket\n"); } /* * Send request */ if (write_uninterrupted(sk, (char*)request_msg, request_msg_size) < 0) fatal("error sending message via Netlink\n"); /* * Wait for reply */ pfd.fd = sk; pfd.events = ~POLLOUT; if ((poll(&pfd, 1, 3000) != 1) || !(pfd.revents & POLLIN)) fatal("no reply detected from Netlink\n"); /* * Read reply */ len = recv(sk, (char*)reply_msg, reply_msg_size, 0); if (len < 0) fatal("error receiving reply message via Netlink\n"); close(sk); /* * Validate response */ if (!NLMSG_OK(&reply_msg->n, len)) fatal("invalid reply message received via Netlink\n"); if (reply_msg->n.nlmsg_type == NLMSG_ERROR) { len = -1; goto out; } if ((request_msg->n.nlmsg_type != reply_msg->n.nlmsg_type) || (request_msg->n.nlmsg_seq != reply_msg->n.nlmsg_seq)) fatal("unexpected message received via Netlink\n"); /* * Copy reply header */ len -= NLMSG_LENGTH(GENL_HDRLEN); if (len < header_len) fatal("too small reply message received via Netlink\n"); if (header_len > 0) memcpy(header, &reply_msg->payload[0], header_len); /* * Copy reply payload */ len -= header_len; if (len > reply_len) fatal("reply message too large to copy\n"); if (len > 0) memcpy(reply, &reply_msg->payload[header_len], len); out: free(request_msg); free(reply_msg); return len; }
/* Our netlink parser */ static int netlink_parse_info(int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), nl_handle_t *nl, struct nlmsghdr *n) { int status; int ret = 0; int error; while (1) { char buf[4096]; struct iovec iov = { buf, sizeof buf }; struct sockaddr_nl snl; struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; struct nlmsghdr *h; status = recvmsg(nl->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) break; log_message(LOG_INFO, "Netlink: Received message overrun (%m)"); continue; } if (status == 0) { log_message(LOG_INFO, "Netlink: EOF"); return -1; } if (msg.msg_namelen != sizeof snl) { log_message(LOG_INFO, "Netlink: Sender address length error: length %d", msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) { /* Finish of reading. */ if (h->nlmsg_type == NLMSG_DONE) return ret; /* Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(h); /* * If error == 0 then this is a netlink ACK. * return if not related to multipart message. */ if (err->error == 0) { if (!(h->nlmsg_flags & NLM_F_MULTI)) return 0; continue; } if (h->nlmsg_len < NLMSG_LENGTH(sizeof (struct nlmsgerr))) { log_message(LOG_INFO, "Netlink: error: message truncated"); return -1; } if (n && (err->error == -EEXIST) && ((n->nlmsg_type == RTM_NEWROUTE) || (n->nlmsg_type == RTM_NEWADDR))) return 0; log_message(LOG_INFO, "Netlink: error: %s, type=(%u), seq=%u, pid=%d", strerror(-err->error), err->msg.nlmsg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); return -1; } /* Skip unsolicited messages from cmd channel */ if (nl != &nl_cmd && h->nlmsg_pid == nl_cmd.snl.nl_pid) continue; error = (*filter) (&snl, h); if (error < 0) { log_message(LOG_INFO, "Netlink: filter function error"); ret = error; } } /* After error care. */ if (msg.msg_flags & MSG_TRUNC) { log_message(LOG_INFO, "Netlink: error: message truncated"); continue; } if (status) { log_message(LOG_INFO, "Netlink: error: data remnant size %d", status); return -1; } } return ret; }
/* * Adds or deletes an IP address on an interface. * * Action is one of: * - RTM_NEWADDR (to add a new address) * - RTM_DELADDR (to delete an existing address) * * Returns zero on success and negative errno on failure. */ int ifc_act_on_address(int action, const char *name, const char *address, int prefixlen) { int ifindex, s, len, ret; struct sockaddr_storage ss; void *addr; size_t addrlen; struct { struct nlmsghdr n; struct ifaddrmsg r; // Allow for IPv6 address, headers, and padding. char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET6_ADDRLEN)]; } req; struct rtattr *rta; struct nlmsghdr *nh; struct nlmsgerr *err; char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct nlmsgerr)) + NLMSG_ALIGN(sizeof(struct nlmsghdr))]; // Get interface ID. ifindex = if_nametoindex(name); if (ifindex == 0) { return -errno; } // Convert string representation to sockaddr_storage. ret = string_to_ip(address, &ss); if (ret) { return ret; } // Determine address type and length. if (ss.ss_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *) &ss; addr = &sin->sin_addr; addrlen = INET_ADDRLEN; } else if (ss.ss_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; addr = &sin6->sin6_addr; addrlen = INET6_ADDRLEN; } else { return -EAFNOSUPPORT; } // Fill in netlink structures. memset(&req, 0, sizeof(req)); // Netlink message header. req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); req.n.nlmsg_type = action; req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_pid = getpid(); // Interface address message header. req.r.ifa_family = ss.ss_family; req.r.ifa_prefixlen = prefixlen; req.r.ifa_index = ifindex; // Routing attribute. Contains the actual IP address. rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len)); rta->rta_type = IFA_LOCAL; rta->rta_len = RTA_LENGTH(addrlen); req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen); memcpy(RTA_DATA(rta), addr, addrlen); s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (send(s, &req, req.n.nlmsg_len, 0) < 0) { close(s); return -errno; } len = recv(s, buf, sizeof(buf), 0); close(s); if (len < 0) { return -errno; } // Parse the acknowledgement to find the return code. nh = (struct nlmsghdr *) buf; if (!NLMSG_OK(nh, (unsigned) len) || nh->nlmsg_type != NLMSG_ERROR) { return -EINVAL; } err = NLMSG_DATA(nh); // Return code is negative errno. return err->error; }
int readPackets(HSP *sp) { int batch = 0; static uint32_t MySkipCount=1; if(sp->sFlow->sFlowSettings->ulogSubSamplingRate == 0) { // packet sampling was disabled by setting desired rate to 0 return 0; } if(sp->ulog_soc) { for( ; batch < HSP_READPACKET_BATCH; batch++) { char buf[HSP_MAX_MSG_BYTES]; socklen_t peerlen = sizeof(sp->ulog_peer); int len = recvfrom(sp->ulog_soc, buf, HSP_MAX_MSG_BYTES, 0, (struct sockaddr *)&sp->ulog_peer, &peerlen); if(len <= 0) break; if(debug > 1) myLog(LOG_INFO, "got ULOG msg: %u bytes", len); for(struct nlmsghdr *msg = (struct nlmsghdr *)buf; NLMSG_OK(msg, len); msg=NLMSG_NEXT(msg, len)) { if(debug > 1) { myLog(LOG_INFO, "netlink (%u bytes left) msg [len=%u type=%u flags=0x%x seq=%u pid=%u]", len, msg->nlmsg_len, msg->nlmsg_type, msg->nlmsg_flags, msg->nlmsg_seq, msg->nlmsg_pid); } switch(msg->nlmsg_type) { case NLMSG_NOOP: case NLMSG_ERROR: case NLMSG_OVERRUN: // ignore these break; case NLMSG_DONE: // last in multi-part default: { if(--MySkipCount == 0) { /* reached zero. Set the next skip */ MySkipCount = sfl_random((2 * sp->sFlow->sFlowSettings->ulogSubSamplingRate) - 1); /* and take a sample */ // we're seeing type==111 on Fedora14 //if(msg->nlmsg_flags & NLM_F_REQUEST) { } //if(msg->nlmsg_flags & NLM_F_MULTI) { } //if(msg->nlmsg_flags & NLM_F_ACK) { } //if(msg->nlmsg_flags & NLM_F_ECHO) { } ulog_packet_msg_t *pkt = NLMSG_DATA(msg); if(debug > 1) { myLog(LOG_INFO, "mark=%u ts=%s hook=%u in=%s out=%s len=%u prefix=%s maclen=%u\n", pkt->mark, ctime(&pkt->timestamp_sec), pkt->hook, pkt->indev_name, pkt->outdev_name, pkt->data_len, pkt->prefix, pkt->mac_len); if(pkt->mac_len == 14) { u_char macdst[12], macsrc[12]; printHex(pkt->mac+6,6,macsrc,12,NO); printHex(pkt->mac+0,6,macdst,12,NO); uint16_t ethtype = (pkt->mac[12] << 8) + pkt->mac[13]; myLog(LOG_INFO, "%s -> %s (ethertype=0x%04X)", macsrc, macdst, ethtype); } } SFL_FLOW_SAMPLE_TYPE fs = { 0 }; SFLSampler *sampler = NULL; // set the ingress and egress ifIndex numbers. // Can be "INTERNAL" (0x3FFFFFFF) or "UNKNOWN" (0). if(pkt->indev_name[0]) { SFLAdaptor *in = adaptorListGet(sp->adaptorList, pkt->indev_name); if(in && in->ifIndex) { fs.input = in->ifIndex; sampler = getSampler(sp, pkt->indev_name, in->ifIndex); } } else { fs.input = SFL_INTERNAL_INTERFACE; } if(pkt->outdev_name[0]) { SFLAdaptor *out = adaptorListGet(sp->adaptorList, pkt->outdev_name); if(out && out->ifIndex) { fs.output = out->ifIndex; sampler = getSampler(sp, pkt->outdev_name, out->ifIndex); } } else { fs.output = SFL_INTERNAL_INTERFACE; } if(sampler == NULL) { // maybe ULOG sent us a packet on device lo(?) if(debug > 1) myLog(LOG_INFO, "dropped sample with no ifIndex\n"); } else { SFLFlow_sample_element hdrElem = { 0 }; hdrElem.tag = SFLFLOW_HEADER; uint32_t FCS_bytes = 4; uint32_t maxHdrLen = sampler->sFlowFsMaximumHeaderSize; hdrElem.flowType.header.frame_length = pkt->data_len + FCS_bytes; hdrElem.flowType.header.stripped = FCS_bytes; u_char hdr[HSP_MAX_HEADER_BYTES]; if(pkt->mac_len == 14) { // set the header_protocol to ethernet and // reunite the mac header and payload in one buffer hdrElem.flowType.header.header_protocol = SFLHEADER_ETHERNET_ISO8023; memcpy(hdr, pkt->mac, 14); maxHdrLen -= 14; uint32_t payloadBytes = (pkt->data_len < maxHdrLen) ? pkt->data_len : maxHdrLen; memcpy(hdr+14, pkt->payload, payloadBytes); hdrElem.flowType.header.header_length = payloadBytes + 14; hdrElem.flowType.header.header_bytes = hdr; hdrElem.flowType.header.frame_length += 14; } else { // no need to copy - just point at the payload u_char ipversion = pkt->payload[0] >> 4; if(ipversion != 4 && ipversion != 6) { if(debug) myLog(LOG_ERR, "received non-IP packet. Encapsulation is unknown"); } else { hdrElem.flowType.header.header_protocol = (ipversion == 4) ? SFLHEADER_IPv4 : SFLHEADER_IPv6; hdrElem.flowType.header.stripped += 14; // assume ethernet was (or will be) the framing hdrElem.flowType.header.header_length = (pkt->data_len < maxHdrLen) ? pkt->data_len : maxHdrLen; hdrElem.flowType.header.header_bytes = pkt->payload; } } SFLADD_ELEMENT(&fs, &hdrElem); // submit the actual sampling rate so it goes out with the sFlow feed // otherwise the sampler object would fill in his own (sub-sampling) rate. uint32_t actualSamplingRate = sp->sFlow->sFlowSettings->ulogActualSamplingRate; fs.sampling_rate = actualSamplingRate; // estimate the sample pool from the samples. Could maybe do this // above with the (possibly more granular) ulogSamplingRate, but then // we would have to look up the sampler object every time, which // might be too expensive in the case where ulogSamplingRate==1. sampler->samplePool += actualSamplingRate; sfl_sampler_writeFlowSample(sampler, &fs); } } } } } } }
static gboolean can_read_data(GIOChannel *chan, GIOCondition cond, gpointer data) { struct netlink_info *netlink = data; struct cmsghdr *cmsg; struct msghdr msg; struct iovec iov; struct nlmsghdr *nlmsg; unsigned char buffer[4096]; unsigned char control[32]; uint32_t group = 0; ssize_t len; int sk; sk = g_io_channel_unix_get_fd(chan); iov.iov_base = buffer; iov.iov_len = sizeof(buffer); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); len = recvmsg(sk, &msg, 0); if (len < 0) return FALSE; util_hexdump('>', buffer, len, netlink->debug_handler, netlink->debug_data); for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { struct nl_pktinfo *pktinfo; if (cmsg->cmsg_level != SOL_NETLINK) continue; if (cmsg->cmsg_type != NETLINK_PKTINFO) continue; pktinfo = (void *) CMSG_DATA(cmsg); group = pktinfo->group; } for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, (uint32_t) len); nlmsg = NLMSG_NEXT(nlmsg, len)) { if (group > 0 && nlmsg->nlmsg_seq == 0) { process_broadcast(netlink, group, nlmsg); continue; } if (nlmsg->nlmsg_pid != netlink->pid) continue; if (nlmsg->nlmsg_flags & NLM_F_MULTI) process_multi(netlink, nlmsg); else process_message(netlink, nlmsg); } return TRUE; }
// man 7 netlink int netlink_parse(int fd, int seq, netlink_cb_t callback, void *data) { int len; int status; unsigned char buf[4096]; struct sockaddr_nl snl; struct msghdr msg; struct iovec iov = { buf, sizeof(buf) }; struct nlmsghdr *nh; memset(&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; msg.msg_name = (void *)&snl; msg.msg_namelen = sizeof(struct sockaddr_nl); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; status = 0; do { len = recvmsg(fd, &msg, 0); dprintf("[%s] recvmsg returned %d", __FUNCTION__, len); if(len <= 0) { status = errno; dprintf("[%s] socket dead? bailing (%s)", __FUNCTION__, strerror(errno)); break; } if(msg.msg_flags & MSG_TRUNC) { dprintf("[%s] truncated message ? :(", __FUNCTION__); status = ERROR_NOT_SUPPORTED; break; } for(nh = (struct nlmsghdr *)(buf); NLMSG_OK(nh, len); nh = (struct nlmsghdr *) NLMSG_NEXT(nh, len)) { dprintf("[%s] buf = %p, nh = %p", __FUNCTION__, buf, nh); dprintf("[%s] nh->nlmsg_type = %d", __FUNCTION__, nh->nlmsg_type); if(nh->nlmsg_type == NLMSG_DONE) break; if(nh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *me = (struct nlmsgerr *) NLMSG_DATA (nh); dprintf("[%s] in NLMSG_ERROR handling.. me = %p", __FUNCTION__, me); dprintf("[%s] me->error = %d", __FUNCTION__, me->error); if(me->error) { dprintf("[%s] so, we have: nlmsg_len: %d, nlmsg_type: %d, nlmsg_flags: %d, nlmsg_seq: %d, nlmsg_pid: %d", __FUNCTION__, me->msg.nlmsg_len, me->msg.nlmsg_type, me->msg.nlmsg_flags, me->msg.nlmsg_seq, me->msg.nlmsg_pid); if(me->msg.nlmsg_seq == seq) { dprintf("[%s] Hum. kernel doesn't like our message :~(", __FUNCTION__); status = ERROR_NOT_SUPPORTED; break; } dprintf("[%s] don't know how to handle this error at the moment. continuing", __FUNCTION__); } continue; // "yea, whatever" } // dprintf("[%s] dispatching into callback", __FUNCTION__); status = callback(nh, data); if(status) { dprintf("[%s] callback returned non zero(%d) , stopping process", __FUNCTION__, status); break; } } } while(0); return status; }