/* Add/Delete IP rule to/from a specific IP/network */ int netlink_rule(ip_rule_t *iprule, int cmd) { int status = 1; struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; req.n.nlmsg_type = cmd ? RTM_NEWRULE : RTM_DELRULE; req.r.rtm_family = IP_FAMILY(iprule->addr); if (iprule->table < 256) req.r.rtm_table = iprule->table ? iprule->table : RT_TABLE_MAIN; else { req.r.rtm_table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), FRA_TABLE, iprule->table); } req.r.rtm_type = RTN_UNSPEC; req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_flags = 0; if (cmd) { req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_type = RTN_UNICAST; } /* Set rule entry */ if (iprule->dir == VRRP_RULE_FROM) { req.r.rtm_src_len = iprule->mask; add_addr2req(&req.n, sizeof(req), FRA_SRC, iprule->addr); } else if (iprule->dir == VRRP_RULE_TO) { req.r.rtm_dst_len = iprule->mask; add_addr2req(&req.n, sizeof(req), FRA_DST, iprule->addr); } if (netlink_talk(&nl_cmd, &req.n) < 0) status = -1; return status; }
/* Add/Delete IP route to/from a specific interface */ static int netlink_route(ip_route_t *iproute, int cmd) { int status = 1; struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; char buf[1024]; struct rtattr *rta = (void*)buf; struct rtnexthop *rtnh; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; req.n.nlmsg_type = cmd ? RTM_NEWROUTE : RTM_DELROUTE; req.r.rtm_family = (iproute->dst) ? IP_FAMILY(iproute->dst) : (iproute->src) ? IP_FAMILY(iproute->src) : AF_INET; if (iproute->table < 256) req.r.rtm_table = iproute->table ? iproute->table : RT_TABLE_MAIN; else { req.r.rtm_table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), RTA_TABLE, iproute->table); } req.r.rtm_scope = RT_SCOPE_NOWHERE; if (cmd == IPROUTE_ADD) { req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_scope = iproute->scope; req.r.rtm_type = RTN_UNICAST; } if (iproute->blackhole) req.r.rtm_type = RTN_BLACKHOLE; /* Set routing entry */ if (iproute->dst) { req.r.rtm_dst_len = iproute->dmask; add_addr2req(&req.n, sizeof(req), RTA_DST, iproute->dst); } if ((!iproute->blackhole) && (!iproute->gw2)) add_addr2req(&req.n, sizeof(req), RTA_GATEWAY, iproute->gw); if (iproute->gw && iproute->gw2) { rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); #define MULTIPATH_ADD_GW(x) \ memset(rtnh, 0, sizeof(*rtnh)); \ rtnh->rtnh_len = sizeof(*rtnh); \ if (iproute->index) rtnh->rtnh_ifindex = iproute->index; \ rta->rta_len += rtnh->rtnh_len; \ add_addr2rta(rta, 1024, RTA_GATEWAY, x); \ rtnh->rtnh_len += sizeof(struct rtattr) + IP_SIZE(x); \ rtnh = RTNH_NEXT(rtnh); MULTIPATH_ADD_GW(iproute->gw); MULTIPATH_ADD_GW(iproute->gw2); addattr_l(&req.n, sizeof(req), RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); } if ((iproute->index) && (!iproute->gw2)) addattr32(&req.n, sizeof(req), RTA_OIF, iproute->index); if (iproute->src) add_addr2req(&req.n, sizeof(req), RTA_PREFSRC, iproute->src); if (iproute->metric) addattr32(&req.n, sizeof(req), RTA_PRIORITY, iproute->metric); /* This returns ESRCH if the address of via address doesn't exist */ /* ENETDOWN if dev p33p1.40 for example is down */ if (netlink_talk(&nl_cmd, &req.n) < 0) status = -1; return status; }
/* Add/Delete IP route to/from a specific interface */ static int netlink_route(ip_route_t *iproute, int cmd) { int status = 1; struct { struct nlmsghdr n; struct rtmsg r; char buf[RTM_SIZE]; } req; char buf[RTA_SIZE]; struct rtattr *rta = (void*)buf; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); if (cmd == IPROUTE_DEL) { req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_DELROUTE; } else { req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; if (cmd == IPROUTE_REPLACE) req.n.nlmsg_flags |= NLM_F_REPLACE; req.n.nlmsg_type = RTM_NEWROUTE; } rta->rta_type = RTA_METRICS; rta->rta_len = RTA_LENGTH(0); req.r.rtm_family = iproute->family; if (iproute->table < 256) req.r.rtm_table = iproute->table; else { req.r.rtm_table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), RTA_TABLE, iproute->table); } if (cmd == IPROUTE_DEL) { req.r.rtm_scope = RT_SCOPE_NOWHERE; if (iproute->mask & IPROUTE_BIT_TYPE) req.r.rtm_type = iproute->type; } else { req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_type = iproute->type; } if (iproute->mask & IPROUTE_BIT_PROTOCOL) req.r.rtm_protocol = iproute->protocol; if (iproute->mask & IPROUTE_BIT_SCOPE) req.r.rtm_scope = iproute->scope; if (iproute->dst) { req.r.rtm_dst_len = iproute->dst->ifa.ifa_prefixlen; add_addr2req(&req.n, sizeof(req), RTA_DST, iproute->dst); } if (iproute->src) { req.r.rtm_src_len = iproute->src->ifa.ifa_prefixlen; add_addr2req(&req.n, sizeof(req), RTA_SRC, iproute->src); } if (iproute->pref_src) add_addr2req(&req.n, sizeof(req), RTA_PREFSRC, iproute->pref_src); //#ifdef _HAVE_RTA_NEWDST_ // if (iproute->as_to) // add_addr2req(&req.n, sizeof(req), RTA_NEWDST, iproute->as_to); //#endif if (iproute->via) { if (iproute->via->ifa.ifa_family == iproute->family) add_addr2req(&req.n, sizeof(req), RTA_GATEWAY, iproute->via); #ifdef _HAVE_RTA_VIA_ else add_addr_fam2req(&req.n, sizeof(req), RTA_VIA, iproute->via); #endif } #ifdef _HAVE_RTA_ENCAP_ if (iproute->encap.type != LWTUNNEL_ENCAP_NONE) { char encap_buf[ENCAP_RTA_SIZE]; struct rtattr *encap_rta = (void *)encap_buf; encap_rta->rta_type = RTA_ENCAP; encap_rta->rta_len = RTA_LENGTH(0); add_encap(encap_rta, sizeof(encap_buf), &iproute->encap); if (encap_rta->rta_len > RTA_LENGTH(0)) addraw_l(&req.n, sizeof(encap_buf), RTA_DATA(encap_rta), RTA_PAYLOAD(encap_rta)); } #endif if (iproute->mask & IPROUTE_BIT_DSFIELD) req.r.rtm_tos = iproute->tos; if (iproute->oif) addattr32(&req.n, sizeof(req), RTA_OIF, iproute->oif->ifindex); if (iproute->mask & IPROUTE_BIT_METRIC) addattr32(&req.n, sizeof(req), RTA_PRIORITY, iproute->metric); req.r.rtm_flags = iproute->flags; if (iproute->realms) addattr32(&req.n, sizeof(req), RTA_FLOW, iproute->realms); #ifdef _HAVE_RTA_EXPIRES_ if (iproute->mask & IPROUTE_BIT_EXPIRES) addattr32(&req.n, sizeof(req), RTA_EXPIRES, iproute->expires); #endif #ifdef RTAX_CC_ALGO if (iproute->congctl) rta_addattr_l(rta, sizeof(buf), RTAX_CC_ALGO, iproute->congctl, strlen(iproute->congctl)); #endif if (iproute->mask & IPROUTE_BIT_RTT) rta_addattr32(rta, sizeof(buf), RTAX_RTT, iproute->rtt); if (iproute->mask & IPROUTE_BIT_RTTVAR) rta_addattr32(rta, sizeof(buf), RTAX_RTTVAR, iproute->rttvar); if (iproute->mask & IPROUTE_BIT_RTO_MIN) rta_addattr32(rta, sizeof(buf), RTAX_RTO_MIN, iproute->rto_min); #ifdef RTAX_FEATURES if (iproute->features) rta_addattr32(rta, sizeof(buf), RTAX_FEATURES, iproute->features); #endif if (iproute->mask & IPROUTE_BIT_MTU) rta_addattr32(rta, sizeof(buf), RTAX_MTU, iproute->mtu); if (iproute->mask & IPROUTE_BIT_WINDOW) rta_addattr32(rta, sizeof(buf), RTAX_WINDOW, iproute->window); if (iproute->mask & IPROUTE_BIT_SSTHRESH) rta_addattr32(rta, sizeof(buf), RTAX_SSTHRESH, iproute->ssthresh); if (iproute->mask & IPROUTE_BIT_CWND) rta_addattr32(rta, sizeof(buf), RTAX_CWND, iproute->cwnd); if (iproute->mask & IPROUTE_BIT_ADVMSS) rta_addattr32(rta, sizeof(buf), RTAX_ADVMSS, iproute->advmss); if (iproute->mask & IPROUTE_BIT_REORDERING) rta_addattr32(rta, sizeof(buf), RTAX_REORDERING, iproute->reordering); if (iproute->mask & IPROUTE_BIT_HOPLIMIT) rta_addattr32(rta, sizeof(buf), RTAX_HOPLIMIT, iproute->hoplimit); if (iproute->mask & IPROUTE_BIT_INITCWND) rta_addattr32(rta, sizeof(buf), RTAX_INITCWND, iproute->initcwnd); #ifdef RTAX_INITRWND if (iproute->mask & IPROUTE_BIT_INITRWND) rta_addattr32(rta, sizeof(buf), RTAX_INITRWND, iproute->initrwnd); #endif #ifdef RTAX_QUICKACK if (iproute->mask & IPROUTE_BIT_QUICKACK) rta_addattr32(rta, sizeof(buf), RTAX_QUICKACK, iproute->quickack); #endif #ifdef _HAVE_RTA_PREF_ if (iproute->mask & IPROUTE_BIT_PREF) addattr8(&req.n, sizeof(req), RTA_PREF, iproute->pref); #endif if (rta->rta_len > RTA_LENGTH(0)) { if (iproute->lock) rta_addattr32(rta, sizeof(buf), RTAX_LOCK, iproute->lock); addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(rta), RTA_PAYLOAD(rta)); } if (!LIST_ISEMPTY(iproute->nhs)) add_nexthops(iproute, &req.n, &req.r); #ifdef DEBUG_NETLINK_MSG size_t i, j; uint8_t *p; char lbuf[3072]; char *op = lbuf; log_message(LOG_INFO, "rtmsg buffer used %lu, rtattr buffer used %d", req.n.nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)), rta->rta_len); op += snprintf(op, sizeof(lbuf) - (op - lbuf), "nlmsghdr %p(%u):", &req.n, req.n.nlmsg_len); for (i = 0, p = (uint8_t*)&req.n; i < sizeof(struct nlmsghdr); i++) op += snprintf(op, sizeof(lbuf) - (op - lbuf), " %2.2hhx", *(p++)); log_message(LOG_INFO, "%s\n", lbuf); op = lbuf; op += snprintf(op, sizeof(lbuf) - (op - lbuf), "rtmsg %p(%lu):", &req.r, req.n.nlmsg_len - sizeof(struct nlmsghdr)); for (i = 0, p = (uint8_t*)&req.r; i < + req.n.nlmsg_len - sizeof(struct nlmsghdr); i++) op += snprintf(op, sizeof(lbuf) - (op - lbuf), " %2.2hhx", *(p++)); for (j = 0; lbuf + j < op; j+= MAX_LOG_MSG) log_message(LOG_INFO, "%.*\n", MAX_LOG_MSG, lbuf+j); #endif /* This returns ESRCH if the address of via address doesn't exist */ /* ENETDOWN if dev p33p1.40 for example is down */ if (netlink_talk(&nl_cmd, &req.n) < 0) { #ifdef _HAVE_RTA_EXPIRES_ /* If an expiry was set on the route, it may have disappeared already */ if (cmd != IPADDRESS_DEL || !(iproute->mask & IPROUTE_BIT_EXPIRES)) #endif status = -1; } return status; }
/* Add/Delete IP rule to/from a specific IP/network */ static int netlink_rule(ip_rule_t *iprule, int cmd) { int status = 1; struct { struct nlmsghdr n; struct fib_rule_hdr frh; char buf[1024]; } req; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; if (cmd != IPRULE_DEL) { req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; req.n.nlmsg_type = RTM_NEWRULE; req.frh.action = FR_ACT_UNSPEC; } else { req.frh.action = FR_ACT_UNSPEC; req.n.nlmsg_type = RTM_DELRULE; } req.frh.table = RT_TABLE_UNSPEC; req.frh.flags = 0; req.frh.tos = iprule->tos; // Hex value - 0xnn <= 255, or name from rt_dsfield req.frh.family = iprule->family; if (iprule->action == FR_ACT_TO_TBL #if HAVE_DECL_FRA_L3MDEV && !iprule->l3mdev #endif ) { if (iprule->table < 256) // "Table" or "lookup" req.frh.table = iprule->table ? iprule->table & 0xff : RT_TABLE_MAIN; else { req.frh.table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), FRA_TABLE, iprule->table); } } if (iprule->invert) req.frh.flags |= FIB_RULE_INVERT; // "not" /* Set rule entry */ if (iprule->from_addr) { // can be "default"/"any"/"all" - and to addr => bytelen == bitlen == 0 add_addr2req(&req.n, sizeof(req), FRA_SRC, iprule->from_addr); req.frh.src_len = iprule->from_addr->ifa.ifa_prefixlen; } if (iprule->to_addr) { add_addr2req(&req.n, sizeof(req), FRA_DST, iprule->to_addr); req.frh.dst_len = iprule->to_addr->ifa.ifa_prefixlen; } if (iprule->mask & IPRULE_BIT_PRIORITY) // "priority/order/preference" addattr32(&req.n, sizeof(req), FRA_PRIORITY, iprule->priority); if (iprule->mask & IPRULE_BIT_FWMARK) // "fwmark" addattr32(&req.n, sizeof(req), FRA_FWMARK, iprule->fwmark); if (iprule->mask & IPRULE_BIT_FWMASK) // "fwmark number followed by /nn" addattr32(&req.n, sizeof(req), FRA_FWMASK, iprule->fwmask); if (iprule->realms) // "realms u16[/u16] using rt_realms. after / is 16 msb (src), pre slash is 16 lsb (dest)" addattr32(&req.n, sizeof(req), FRA_FLOW, iprule->realms); #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN if (iprule->suppress_prefix_len != -1) // "suppress_prefixlength" - only valid if table != 0 addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, iprule->suppress_prefix_len); #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP if (iprule->mask & IPRULE_BIT_SUP_GROUP) // "suppress_ifgroup" or "sup_group" int32 - only valid if table !=0 addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, iprule->suppress_group); #endif if (iprule->iif) // "dev/iif" addattr_l(&req.n, sizeof(req), FRA_IFNAME, iprule->iif, strlen(iprule->iif->ifname)+1); #if HAVE_DECL_FRA_OIFNAME if (iprule->oif) // "oif" addattr_l(&req.n, sizeof(req), FRA_OIFNAME, iprule->oif, strlen(iprule->oif->ifname)+1); #endif #if HAVE_DECL_FRA_TUN_ID if (iprule->tunnel_id) addattr64(&req.n, sizeof(req), FRA_TUN_ID, htobe64(iprule->tunnel_id)); #endif #if HAVE_DECL_FRA_UID_RANGE if (iprule->mask & IPRULE_BIT_UID_RANGE) addattr_l(&req.n, sizeof(req), FRA_UID_RANGE, &iprule->uid_range, sizeof(iprule->uid_range)); #endif #if HAVE_DECL_FRA_L3MDEV if (iprule->l3mdev) addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1); #endif #if HAVE_DECL_FRA_PROTOCOL if (iprule->mask & IPRULE_BIT_PROTOCOL) addattr8(&req.n, sizeof(req), FRA_PROTOCOL, iprule->protocol); #endif #if HAVE_DECL_FRA_IP_PROTO if (iprule->mask & IPRULE_BIT_IP_PROTO) addattr8(&req.n, sizeof(req), FRA_IP_PROTO, iprule->ip_proto); #endif #if HAVE_DECL_FRA_SPORT_RANGE if (iprule->mask & IPRULE_BIT_SPORT_RANGE) addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &iprule->src_port, sizeof(iprule->src_port)); #endif #if HAVE_DECL_FRA_DPORT_RANGE if (iprule->mask & IPRULE_BIT_DPORT_RANGE) addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &iprule->dst_port, sizeof(iprule->dst_port)); #endif if (iprule->action == FR_ACT_GOTO) { // "goto" addattr32(&req.n, sizeof(req), FRA_GOTO, iprule->goto_target); req.frh.action = FR_ACT_GOTO; } req.frh.action = iprule->action; if (netlink_talk(&nl_cmd, &req.n) < 0) status = -1; return status; }