diff options
-rw-r--r-- | accel-pppd/accel-ppp.conf | 1 | ||||
-rw-r--r-- | accel-pppd/accel-ppp.conf.5 | 3 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 83 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.h | 2 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe_netlink.c | 71 | ||||
-rw-r--r-- | drivers/ipoe/ipoe.c | 96 | ||||
-rw-r--r-- | drivers/ipoe/ipoe.h | 2 |
7 files changed, 253 insertions, 5 deletions
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf index bd58a7a..6b6352e 100644 --- a/accel-pppd/accel-ppp.conf +++ b/accel-pppd/accel-ppp.conf @@ -143,6 +143,7 @@ start=dhcpv4 #soft-terminate=0 #check-mac-change=1 #calling-sid=mac +#local-net=192.168.0.0/16 interface=eth0 diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5 index 4deec4e..648fd24 100644 --- a/accel-pppd/accel-ppp.conf.5 +++ b/accel-pppd/accel-ppp.conf.5 @@ -354,6 +354,9 @@ The .B proxy-arp parameter specifies whether accel-ppp should reply to arp requests. .TP +.BI "local-net=" x.x.x.x/mask +Specifies networks from which packets will be treated as unclassified. You may specify multiple local-net options. +.TP .BI "proto=" n Specifies number of protocol to be used for inserted routes. .TP diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 63df066..49e32a7 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -89,6 +89,13 @@ struct request_item { int cnt; }; +struct local_net { + struct list_head entry; + in_addr_t addr; + int mask; + int active; +}; + enum {SID_MAC, SID_IP}; static int conf_dhcpv4 = 1; @@ -171,6 +178,8 @@ static LIST_HEAD(uc_list); static int uc_size; static mempool_t uc_pool; +static LIST_HEAD(local_nets); + static pthread_rwlock_t l4_list_lock = PTHREAD_RWLOCK_INITIALIZER; static LIST_HEAD(l4_redirect_list); static struct triton_timer_t l4_redirect_timer; @@ -3019,6 +3028,79 @@ static void load_vlan_mon(struct conf_sect_t *sect) } } +static void parse_local_net(const char *opt) +{ + const char *ptr; + char str[17]; + in_addr_t addr; + int mask; + char *endptr; + struct local_net *n; + + ptr = strchr(opt, '/'); + if (ptr) { + memcpy(str, opt, ptr - opt); + str[ptr - opt] = 0; + addr = inet_addr(str); + if (addr == INADDR_NONE) + goto out_err; + mask = strtoul(ptr + 1, &endptr, 10); + if (mask > 32) + goto out_err; + } else { + addr = inet_addr(opt); + if (addr == INADDR_NONE) + goto out_err; + mask = 24; + } + + list_for_each_entry(n, &local_nets, entry) { + if (n->addr == addr && n->mask == mask) { + n->active = 1; + return; + } + } + + n = _malloc(sizeof(*n)); + n->addr = addr; + n->mask = mask; + n->active = 1; + list_add_tail(&n->entry, &local_nets); + + ipoe_nl_add_net(addr, mask); + + return; + +out_err: + log_error("ipoe: failed to parse 'local-net=%s'\n", opt); +} + +static void load_local_nets(struct conf_sect_t *sect) +{ + struct conf_option_t *opt; + struct local_net *n; + struct list_head *pos, *t; + + list_for_each_entry(n, &local_nets, entry) + n->active = 0; + + list_for_each_entry(opt, §->items, entry) { + if (strcmp(opt->name, "local-net")) + continue; + if (!opt->val) + continue; + parse_local_net(opt->val); + } + + list_for_each_safe(pos, t, &local_nets) { + n = list_entry(pos, typeof(*n), entry); + if (!n->active) { + ipoe_nl_del_net(n->addr); + list_del(&n->entry); + _free(n); + } + } +} static void load_config(void) { @@ -3280,6 +3362,7 @@ static void load_config(void) load_interfaces(s); load_vlan_mon(s); load_gw_addr(s); + load_local_nets(s); } static struct triton_context_t l4_redirect_ctx = { diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index 5561646..825ffca 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -128,6 +128,8 @@ int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, uint32_t gw, void ipoe_nl_get_sessions(struct list_head *list); int ipoe_nl_add_exclude(uint32_t addr, int mask); void ipoe_nl_del_exclude(uint32_t addr); +int ipoe_nl_add_net(uint32_t addr, int mask); +void ipoe_nl_del_net(uint32_t addr); void *arpd_start(struct ipoe_serv *ipoe); void arpd_stop(void *arp); diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c index 7e61fb8..9372a94 100644 --- a/accel-pppd/ctrl/ipoe/ipoe_netlink.c +++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c @@ -98,6 +98,76 @@ void ipoe_nl_del_exclude(uint32_t addr) rtnl_close(&rth); } +int ipoe_nl_add_net(uint32_t addr, int mask) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + struct { + struct nlmsghdr n; + char buf[1024]; + } req; + int ret = 0; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) { + log_ppp_error("ipoe: cannot open generic netlink socket\n"); + return -1; + } + + nlh = &req.n; + nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_type = ipoe_genl_id; + + ghdr = NLMSG_DATA(&req.n); + ghdr->cmd = IPOE_CMD_ADD_NET; + + mask = mask ? ~0 << (32 - mask) : 0; + + addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr); + addattr32(nlh, 1024, IPOE_ATTR_MASK, mask); + + if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 ) { + log_ppp_error("ipoe: nl_add_net: error talking to kernel\n"); + ret = -1; + } + + rtnl_close(&rth); + + return ret; +} + +void ipoe_nl_del_net(uint32_t addr) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + struct { + struct nlmsghdr n; + char buf[1024]; + } req; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) { + log_ppp_error("ipoe: cannot open generic netlink socket\n"); + return; + } + + nlh = &req.n; + nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_type = ipoe_genl_id; + + ghdr = NLMSG_DATA(&req.n); + ghdr->cmd = IPOE_CMD_DEL_NET; + + addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr); + + if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 ) + log_ppp_error("ipoe: nl_del_net: error talking to kernel\n"); + + rtnl_close(&rth); +} + void ipoe_nl_add_interface(int ifindex, uint8_t mode) { struct rtnl_handle rth; @@ -544,6 +614,7 @@ static void init(void) triton_context_wakeup(&mc_ctx); ipoe_nl_del_exclude(0); + ipoe_nl_del_net(0); ipoe_nl_delete_interfaces(); } diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 182b3d9..7eccf6e 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -108,6 +108,7 @@ static LIST_HEAD(ipoe_list2); static LIST_HEAD(ipoe_list2_u); static DEFINE_SEMAPHORE(ipoe_wlock); static LIST_HEAD(ipoe_interfaces); +static LIST_HEAD(ipoe_networks); static struct work_struct ipoe_queue_work; static struct sk_buff_head ipoe_queue; @@ -159,6 +160,29 @@ static void ipoe_update_stats(struct sk_buff *skb, struct ipoe_stats *st, int co u64_stats_update_end(&st->sync); } +static int ipoe_check_network(__be32 addr) +{ + struct ipoe_network *n; + int r; + + if (list_empty(&ipoe_networks)) + return 1; + + r = 0; + addr = ntohl(addr); + + rcu_read_lock(); + list_for_each_entry_rcu(n, &ipoe_networks, entry) { + if ((addr & n->mask) == n->addr) { + r = 1; + break; + } + } + rcu_read_unlock(); + + return r; +} + static int ipoe_check_exclude(__be32 addr) { struct ipoe_network *n; @@ -689,8 +713,11 @@ static rx_handler_result_t ipoe_recv(struct sk_buff **pskb) if (ipoe_check_exclude(iph->saddr)) return RX_HANDLER_PASS; - if (ipoe_queue_u(skb, iph->saddr)) - kfree_skb(skb); + if (ipoe_check_network(iph->saddr)) { + if (ipoe_queue_u(skb, iph->saddr)) + kfree_skb(skb); + } else + return RX_HANDLER_PASS; return RX_HANDLER_CONSUMED; } @@ -1369,13 +1396,12 @@ static int ipoe_nl_cmd_del_exclude(struct sk_buff *skb, struct genl_info *info) if (n->addr == addr) { list_del_rcu(&n->entry); kfree_rcu(n, rcu_head); + break; } } rcu_read_unlock(); up(&ipoe_wlock); - synchronize_rcu(); - return 0; } @@ -1444,7 +1470,7 @@ static int ipoe_nl_cmd_del_interface(struct sk_buff *skb, struct genl_info *info if (ifindex == -1 || ifindex == i->ifindex) { dev = __dev_get_by_index(&init_net, i->ifindex); - if (dev) + if (dev && rcu_dereference(dev->rx_handler) == ipoe_recv) netdev_rx_handler_unregister(dev); list_del(&i->entry); @@ -1460,6 +1486,54 @@ static int ipoe_nl_cmd_del_interface(struct sk_buff *skb, struct genl_info *info return 0; } +static int ipoe_nl_cmd_add_net(struct sk_buff *skb, struct genl_info *info) +{ + struct ipoe_network *n; + + if (!info->attrs[IPOE_ATTR_ADDR] || !info->attrs[IPOE_ATTR_MASK]) + return -EINVAL; + + n = kmalloc(sizeof(*n), GFP_KERNEL); + if (!n) + return -ENOMEM; + + n->addr = nla_get_u32(info->attrs[IPOE_ATTR_ADDR]); + n->mask = nla_get_u32(info->attrs[IPOE_ATTR_MASK]); + n->addr = ntohl(n->addr) & n->mask; + + down(&ipoe_wlock); + list_add_tail_rcu(&n->entry, &ipoe_networks); + up(&ipoe_wlock); + + return 0; +} + +static int ipoe_nl_cmd_del_net(struct sk_buff *skb, struct genl_info *info) +{ + struct ipoe_network *n; + __be32 addr; + + if (!info->attrs[IPOE_ATTR_ADDR]) + return -EINVAL; + + addr = ntohl(nla_get_u32(info->attrs[IPOE_ATTR_ADDR])); + + down(&ipoe_wlock); + rcu_read_lock(); + list_for_each_entry_rcu(n, &ipoe_networks, entry) { + if (!addr || (addr & n->mask) == n->addr) { + list_del_rcu(&n->entry); + kfree_rcu(n, rcu_head); + if (addr) + break; + } + } + rcu_read_unlock(); + up(&ipoe_wlock); + + return 0; +} + static struct nla_policy ipoe_nl_policy[IPOE_ATTR_MAX + 1] = { [IPOE_ATTR_NONE] = { .type = NLA_UNSPEC, }, [IPOE_ATTR_ADDR] = { .type = NLA_U32, }, @@ -1526,6 +1600,18 @@ static struct genl_ops ipoe_nl_ops[] = { .policy = ipoe_nl_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = IPOE_CMD_ADD_NET, + .doit = ipoe_nl_cmd_add_net, + .policy = ipoe_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IPOE_CMD_DEL_NET, + .doit = ipoe_nl_cmd_del_net, + .policy = ipoe_nl_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_family ipoe_nl_family = { diff --git a/drivers/ipoe/ipoe.h b/drivers/ipoe/ipoe.h index c37cb73..c735c2c 100644 --- a/drivers/ipoe/ipoe.h +++ b/drivers/ipoe/ipoe.h @@ -14,6 +14,8 @@ enum { IPOE_REP_PKT, IPOE_CMD_ADD_EXCLUDE, IPOE_CMD_DEL_EXCLUDE, + IPOE_CMD_ADD_NET, + IPOE_CMD_DEL_NET, __IPOE_CMD_MAX, }; |