diff options
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 183 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.h | 7 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe_netlink.c | 57 | ||||
-rw-r--r-- | accel-pppd/ifcfg.c | 8 | ||||
-rw-r--r-- | accel-pppd/session.c | 22 | ||||
-rw-r--r-- | drivers/ipoe/ipoe.c | 180 |
6 files changed, 344 insertions, 113 deletions
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index eedd8cf..5878b0c 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -37,7 +37,13 @@ #define USERNAME_IFNAME 0 #define USERNAME_LUA 1 +#define MODE_L2 0 +#define MODE_L3 1 + static int conf_dhcpv4 = 1; +static int conf_up = 0; +static int conf_mode = 0; +static int conf_shared = 1; //static int conf_dhcpv6; static int conf_username; @@ -51,7 +57,6 @@ static int conf_netmask = 24; static int conf_lease_time = 600; static int conf_lease_timeout = 660; static int conf_verbose; -static int conf_opt_single = 0; static unsigned int stat_starting; static unsigned int stat_active; @@ -158,10 +163,10 @@ static void ipoe_session_start(struct ipoe_session *ses) char *passwd; struct ifreq ifr; - if (ses->serv->opt_single) + if (ses->serv->opt_shared == 0) strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN); else { - ses->ifindex = ipoe_nl_create(0, 0, ses->serv->ifname, ses->hwaddr); + ses->ifindex = ipoe_nl_create(0, 0, ses->dhcpv4_request ? ses->serv->ifname : NULL, ses->hwaddr); if (ses->ifindex == -1) { log_ppp_error("ipoe: failed to create interface\n"); ipoe_session_finished(&ses->ses); @@ -178,6 +183,7 @@ static void ipoe_session_start(struct ipoe_session *ses) } strncpy(ses->ses.ifname, ifr.ifr_name, AP_IFNAME_LEN); + ses->ses.ifindex = ses->ifindex; } if (!ses->ses.username) { @@ -236,6 +242,11 @@ static void ipoe_session_start(struct ipoe_session *ses) ses->timer.expire = ipoe_session_timeout; ses->timer.expire_tv.tv_sec = conf_offer_timeout; triton_timer_add(&ses->ctx, &ses->timer, 0); + } else { + if (ipoe_nl_modify(ses->ifindex, ses->giaddr, ses->ses.ipv4->peer_addr, NULL, NULL)) + ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 0); + else + ap_session_activate(&ses->ses); } } @@ -493,7 +504,7 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p dhcpv4_print_packet(pack, log_ppp_info2); } - if (serv->opt_single) + if (serv->opt_shared == 0) ipoe_drop_sessions(serv, ses); if (ses->ses.state == AP_STATE_STARTING && !ses->dhcpv4_request) { @@ -552,7 +563,9 @@ static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struc ses->ctrl.terminate = ipoe_session_terminate; ses->ctrl.type = CTRL_TYPE_IPOE; ses->ctrl.name = "ipoe"; - + + ses->giaddr = iph->saddr; + ses->ctrl.calling_station_id = _malloc(17); ses->ctrl.called_station_id = _malloc(17); @@ -585,10 +598,13 @@ void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph) list_for_each_entry(serv, &serv_list, entry) { if (serv->ifindex != ifindex) continue; + + if (!serv->opt_up) + return; pthread_mutex_lock(&serv->lock); list_for_each_entry(ses, &serv->sessions, entry) { - if (memcmp(ses->hwaddr, eth->h_source, 6) == 0) { + if (ses->giaddr == iph->saddr) { pthread_mutex_unlock(&serv->lock); return; } @@ -649,42 +665,105 @@ static void ipoe_drop_sessions(struct ipoe_serv *serv, struct ipoe_session *skip static void add_interface(const char *ifname, int ifindex, const char *opt) { - int opt_single; - const char *ptr; + char *str0, *str, *ptr1, *ptr2; + int end; struct ipoe_serv *serv; + int opt_shared = conf_shared; + int opt_dhcpv4 = 0; + int opt_up = 0; + int opt_mode = conf_mode; + + str0 = strchr(opt, ','); + if (str0) { + str0 = _strdup(str0 + 1); + str = str0; + + while (1) { + for (ptr1 = str + 1; *ptr1 && *ptr1 != '='; ptr1++); - ptr = strstr(opt, ",single"); - if (ptr) { - if (ptr[7] && ptr[7] != ',') - goto out_err_parse; - opt_single = 1; - } else { - ptr = strstr(opt, ",shared"); - if (ptr) { - if (ptr[7] && ptr[7] != ',') - goto out_err_parse; - opt_single = 0; - } else - opt_single = conf_opt_single; + if (!*ptr1) + goto parse_err; + + *ptr1 = 0; + + for (ptr2 = ++ptr1; *ptr2 && *ptr2 != ','; ptr2++); + + end = *ptr2 == 0; + + if (!end) + *ptr2 = 0; + + if (ptr2 == ptr1) + goto parse_err; + + if (strcmp(str, "start") == 0) { + if (!strcmp(ptr1, "up")) + opt_up = 1; + else if (!strcmp(ptr1, "dhcpv4")) + opt_dhcpv4 = 1; + else + goto parse_err; + } else if (strcmp(str, "shared") == 0) { + opt_shared = atoi(ptr1); + } else if (strcmp(str, "mode") == 0) { + if (!strcmp(ptr1, "L2")) + opt_mode = MODE_L2; + else if (!strcmp(ptr1, "L3")) + opt_mode = MODE_L3; + else + goto parse_err; + } else + goto parse_err; + + if (end) + break; + + str = ptr2 + 1; + } + + _free(str0); } - + + if (!opt_up && !opt_dhcpv4) { + opt_up = conf_up; + opt_dhcpv4 = conf_dhcpv4; + } + list_for_each_entry(serv, &serv_list, entry) { - if (strcmp(ifname, serv->ifname) == 0) { - serv->active = 1; - serv->ifindex = ifindex; - if (opt_single && !serv->opt_single) - ipoe_drop_sessions(serv, NULL); - serv->opt_single = opt_single; - return; + if (!strcmp(ifname, serv->ifname)) + continue; + + serv->active = 1; + serv->ifindex = ifindex; + + if ((opt_shared && !serv->opt_shared) || (!opt_shared && serv->opt_shared)) { + ipoe_drop_sessions(serv, NULL); + serv->opt_shared = opt_shared; } + + if (opt_dhcpv4 && !serv->dhcpv4) { + serv->dhcpv4 = dhcpv4_create(&serv->ctx, serv->ifname); + if (serv->dhcpv4) + serv->dhcpv4->recv = ipoe_recv_dhcpv4; + } else if (!opt_dhcpv4 && serv->dhcpv4) { + dhcpv4_free(serv->dhcpv4); + serv->dhcpv4 = NULL; + } + + serv->opt_up = opt_up; + serv->opt_mode = conf_mode; + + return; } serv = _malloc(sizeof(*serv)); memset(serv, 0, sizeof(*serv)); serv->ifname = _strdup(ifname); serv->ifindex = ifindex; - serv->opt_single = opt_single; - serv->opt_dhcpv4 = conf_dhcpv4; + serv->opt_shared = opt_shared; + serv->opt_dhcpv4 = opt_dhcpv4; + serv->opt_up = opt_up; + serv->opt_mode = opt_mode; serv->active = 1; INIT_LIST_HEAD(&serv->sessions); pthread_mutex_init(&serv->lock, NULL); @@ -703,8 +782,9 @@ static void add_interface(const char *ifname, int ifindex, const char *opt) return; -out_err_parse: +parse_err: log_error("ipoe: failed to parse '%s'\n", opt); + _free(str0); } static void load_interface(const char *opt) @@ -853,13 +933,11 @@ static void load_config(void) { const char *opt; struct conf_sect_t *s = conf_get_section("ipoe"); + struct conf_option_t *opt1; if (!s) return; - load_interfaces(s); - load_local_nets(s); - opt = conf_get_opt("ipoe", "username"); if (opt) { if (strcmp(opt, "ifname") == 0) @@ -901,6 +979,41 @@ static void load_config(void) opt = conf_get_opt("ipoe", "lease-timeout"); if (opt) conf_lease_timeout = atoi(opt); + + opt = conf_get_opt("ipoe", "shared"); + if (opt) + conf_shared = atoi(opt); + else + conf_shared = 1; + + opt = conf_get_opt("ipoe", "mode"); + if (opt) { + if (!strcmp(opt, "L2")) + conf_mode = MODE_L2; + else if (!strcmp(opt, "L3")) + conf_mode = MODE_L3; + else + log_emerg("ipoe: failed to parse 'mode=%s'\n", opt); + } else + conf_mode = MODE_L2; + + conf_dhcpv4 = 0; + conf_up = 0; + + list_for_each_entry(opt1, &s->items, entry) { + if (strcmp(opt1->name, "start")) + continue; + if (!strcmp(opt1->val, "dhcpv4")) + conf_dhcpv4 = 1; + else if (!strcmp(opt1->val, "up")) + conf_up = 1; + } + + if (!conf_dhcpv4 && !conf_up) + conf_dhcpv4 = 1; + + load_interfaces(s); + load_local_nets(s); } static void ipoe_init(void) diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index 3076090..97b13e6 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -15,11 +15,13 @@ struct ipoe_serv char *ifname; int ifindex; int active; - int opt_single; - int opt_dhcpv4; struct list_head sessions; struct dhcpv4_serv *dhcpv4; pthread_mutex_t lock; + int opt_mode; + int opt_shared:1; + int opt_dhcpv4:1; + int opt_up:1; }; struct dhcp_opt @@ -60,6 +62,7 @@ void ipoe_nl_add_net(uint32_t addr, int mask); void ipoe_nl_delete_nets(void); int ipoe_nl_create(uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr); void ipoe_nl_delete(int ifindex); +int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr); #endif diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c index 57da0de..ec603d5 100644 --- a/accel-pppd/ctrl/ipoe/ipoe_netlink.c +++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c @@ -109,7 +109,7 @@ int ipoe_nl_create(uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_ nlh->nlmsg_type = ipoe_genl_id; ghdr = NLMSG_DATA(&req.n); - ghdr->cmd = IPOE_CMD_DELETE; + ghdr->cmd = IPOE_CMD_CREATE; if (peer_addr) addattr32(nlh, 1024, IPOE_ATTR_PEER_ADDR, peer_addr); @@ -163,6 +163,61 @@ out: return ret; } +int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + int ret = 0; + struct { + struct nlmsghdr n; + char buf[1024]; + } req; + union { + uint8_t hwaddr[6]; + uint64_t u64; + } u; + + 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_MODIFY; + + addattr32(nlh, 1024, IPOE_ATTR_IFINDEX, ifindex); + + if (peer_addr) + addattr32(nlh, 1024, IPOE_ATTR_PEER_ADDR, peer_addr); + + if (addr) + addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr); + + if (hwaddr) { + memcpy(u.hwaddr, hwaddr, 6); + addattr_l(nlh, 1024, IPOE_ATTR_HWADDR, &u.u64, 8); + } + + if (ifname) + addattr_l(nlh, 1024, IPOE_ATTR_IFNAME, ifname, strlen(ifname) + 1); + + if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 ) { + log_ppp_error("ipoe: nl_create: error talking to kernel\n"); + ret = -1; + } + + rtnl_close(&rth); + + return ret; +} + + void ipoe_nl_delete(int ifindex) { struct rtnl_handle rth; diff --git a/accel-pppd/ifcfg.c b/accel-pppd/ifcfg.c index 11251e1..134edfb 100644 --- a/accel-pppd/ifcfg.c +++ b/accel-pppd/ifcfg.c @@ -82,16 +82,16 @@ void ap_session_ifup(struct ap_session *ses) if (ioctl(sock_fd, SIOCSIFADDR, &ifr)) log_ppp_error("failed to set IPv4 address: %s\n", strerror(errno)); - if (ses->ctrl->type == CTRL_TYPE_IPOE) { + /*if (ses->ctrl->type == CTRL_TYPE_IPOE) { addr.sin_addr.s_addr = 0xffffffff; memcpy(&ifr.ifr_netmask, &addr, sizeof(addr)); if (ioctl(sock_fd, SIOCSIFNETMASK, &ifr)) log_ppp_error("failed to set IPv4 nask: %s\n", strerror(errno)); - } + }*/ addr.sin_addr.s_addr = ses->ipv4->peer_addr; - if (ses->ctrl->type == CTRL_TYPE_IPOE) { + /*if (ses->ctrl->type == CTRL_TYPE_IPOE) { memset(&rt, 0, sizeof(rt)); memcpy(&rt.rt_dst, &addr, sizeof(addr)); rt.rt_flags = RTF_HOST | RTF_UP; @@ -99,7 +99,7 @@ void ap_session_ifup(struct ap_session *ses) rt.rt_dev = ifr.ifr_name; if (ioctl(sock_fd, SIOCADDRT, &rt, sizeof(rt))) log_ppp_error("failed to add route: %s\n", strerror(errno)); - } else { + } else*/ { memcpy(&ifr.ifr_dstaddr, &addr, sizeof(addr)); if (ioctl(sock_fd, SIOCSIFDSTADDR, &ifr)) diff --git a/accel-pppd/session.c b/accel-pppd/session.c index 0064f54..886aa40 100644 --- a/accel-pppd/session.c +++ b/accel-pppd/session.c @@ -17,6 +17,7 @@ #include "log.h" #include "events.h" #include "ap_session.h" +#include "spinlock.h" #include "mempool.h" #include "memdebug.h" @@ -24,6 +25,10 @@ int conf_sid_ucase; pthread_rwlock_t __export ses_lock = PTHREAD_RWLOCK_INITIALIZER; __export LIST_HEAD(ses_list); +#if __WORDSIZE == 32 +static spinlock_t seq_lock; +#endif + int __export sock_fd; int __export sock6_fd; int __export urandom_fd; @@ -39,6 +44,7 @@ void __export ap_session_init(struct ap_session *ses) { memset(ses, 0, sizeof(*ses)); INIT_LIST_HEAD(&ses->pd_list); + ses->ifindex = -1; } int __export ap_session_starting(struct ap_session *ses) @@ -47,14 +53,16 @@ int __export ap_session_starting(struct ap_session *ses) ses->start_time = time(NULL); - memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, ses->ifname); - - if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) { - log_ppp_error("ioctl(SIOCGIFINDEX): %s\n", strerror(errno)); - return -1; + if (ses->ifindex == -1) { + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, ses->ifname); + + if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) { + log_ppp_error("ioctl(SIOCGIFINDEX): %s\n", strerror(errno)); + return -1; + } + ses->ifindex = ifr.ifr_ifindex; } - ses->ifindex = ifr.ifr_ifindex; generate_sessionid(ses); diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 1776a44..d9ba4f4 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -335,6 +335,7 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev) struct net_device_stats *stats = &dev->stats; struct iphdr *iph; struct ethhdr *eth; + struct sk_buff *skb1; /*struct arphdr *arp; unsigned char *arp_ptr; __be32 tip;*/ @@ -342,7 +343,7 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev) if (!ses->peer_addr) goto drop; - + noff = skb_network_offset(skb); if (skb->protocol == htons(ETH_P_IP)) { @@ -361,6 +362,13 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev) #endif if (iph->daddr == ses->addr) { + if (skb_shared(skb)) { + skb1 = skb_clone(skb, GFP_ATOMIC); + if (!skb1) + goto drop; + skb = skb1; + } + if (ipoe_do_nat(skb, ses->peer_addr, 1)) goto drop; @@ -406,12 +414,14 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev) } } }*/ - - skb->dev = ses->link_dev; - //skb->skb_iif = dev->ifindex; - dev_queue_xmit(skb); + + if (ses->link_dev) { + skb->dev = ses->link_dev; + //skb->skb_iif = dev->ifindex; + dev_queue_xmit(skb); - return NETDEV_TX_OK; + return NETDEV_TX_OK; + } drop: stats->tx_dropped++; dev_kfree_skb(skb); @@ -502,6 +512,7 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t { struct ipoe_session *ses = NULL; struct iphdr *iph; + struct ethhdr *eth; int noff; struct sk_buff *skb1; unsigned char *cb_ptr; @@ -538,6 +549,12 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t //pr_info("ipoe: recv cb=%x\n", *(__u16 *)cb_ptr); + if (ses->link_dev) { + eth = eth_hdr(skb); + if (memcmp(eth->h_source, ses->hwaddr, ETH_ALEN)) + goto drop_unlock; + } + stats = &ses->dev->stats; if (skb->dev == ses->dev) { @@ -545,9 +562,11 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t atomic_dec(&ses->refs); goto drop; } - - if (ses->addr && !ipoe_check_network(iph->daddr) && ipoe_do_nat(skb, ses->addr, 0)) - goto drop_unlock; + + if (ses->addr && ipoe_check_network(iph->daddr)) { + atomic_dec(&ses->refs); + goto drop; + } skb1 = skb_clone(skb, GFP_ATOMIC); if (!skb1) { @@ -555,8 +574,13 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t goto drop_unlock; } + if (ses->addr && ipoe_do_nat(skb1, ses->addr, 0)) { + kfree_skb(skb1); + goto drop_unlock; + } + skb1->dev = ses->dev; - skb1->skb_iif = ses->dev->ifindex; + //skb1->skb_iif = ses->dev->ifindex; cb_ptr = skb1->cb + sizeof(skb1->cb) - 2; *(__u16 *)cb_ptr = IPOE_MAGIC; @@ -619,7 +643,7 @@ static void ipoe_queue_u(struct sk_buff *skb, __u32 addr) unsigned long ts; if (ipoe_lookup1_u(addr, &ts) && jiffies_to_msecs(jiffies - ts) < IPOE_RATE_U) { - pr_info("not queue %08x\n", addr); + //pr_info("not queue %08x\n", addr); return; } @@ -630,7 +654,7 @@ static void ipoe_queue_u(struct sk_buff *skb, __u32 addr) if (!skb) return; - pr_info("queue %08x\n", addr); + //pr_info("queue %08x\n", addr); skb_queue_tail(&ipoe_queue, skb); schedule_work(&ipoe_queue_work); @@ -667,15 +691,15 @@ static void ipoe_process_queue(struct work_struct *w) list_add_tail_rcu(&e->entry1, &ipoe_list1_u[hash_addr(iph->saddr)]); list_add_tail(&e->entry2, &ipoe_list2_u); - pr_info("create %08x\n", e->addr); + //pr_info("create %08x\n", e->addr); } else if (jiffies_to_msecs(jiffies - e->tstamp) < IPOE_RATE_U) { - pr_info("skip %08x\n", e->addr); + //pr_info("skip %08x\n", e->addr); kfree_skb(skb); continue; } else { e->tstamp = jiffies; list_move_tail(&e->entry2, &ipoe_list2_u); - pr_info("update %08x\n", e->addr); + //pr_info("update %08x\n", e->addr); } if (!report_skb) { @@ -720,7 +744,7 @@ nl_err: if (jiffies_to_msecs(jiffies - e->tstamp) < IPOE_TIMEOUT_U * 1000) break; - pr_info("free %08x\n", e->addr); + //pr_info("free %08x\n", e->addr); list_del(&e->entry2); list_del_rcu(&e->entry1); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) @@ -754,8 +778,9 @@ static struct ipoe_session *ipoe_lookup(__be32 addr) rcu_read_lock(); list_for_each_entry_rcu(ses, head, entry) { - if (ses->addr == addr) { + if (ses->peer_addr == addr) { atomic_inc(&ses->refs); + rcu_read_unlock(); return ses; } } @@ -859,11 +884,11 @@ static void ipoe_netdev_setup(struct net_device *dev) dev->type = ARPHRD_ETHER; dev->hard_header_len = 0; dev->mtu = ETH_DATA_LEN; - dev->flags = 0;//IFF_NOARP | IFF_BROADCAST; + dev->flags = IFF_MULTICAST | IFF_POINTOPOINT; dev->iflink = 0; dev->addr_len = ETH_ALEN; - dev->features = 0;//|= NETIF_F_NETNS_LOCAL; - dev->header_ops = &ipoe_hard_header_ops, + dev->features |= NETIF_F_NETNS_LOCAL; + dev->header_ops = &ipoe_hard_header_ops, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; #endif @@ -922,9 +947,9 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, c } if (addr) - dev->flags = IFF_NOARP; + dev->flags |= IFF_NOARP; else - dev->flags = IFF_BROADCAST; + dev->flags &= ~IFF_NOARP; rtnl_lock(); r = register_netdevice(dev); @@ -991,23 +1016,19 @@ static int ipoe_nl_cmd_create(struct sk_buff *skb, struct genl_info *info) int ret = 0; char ifname[IFNAMSIZ]; __u8 hwaddr[ETH_ALEN]; + struct ipoe_session *ses; //struct net *net = genl_info_net(info); - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) { - ret = -ENOMEM; - goto out; - } - - hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, - &ipoe_nl_family, 0, IPOE_CMD_CREATE); - if (IS_ERR(hdr)) { - ret = PTR_ERR(hdr); - goto err_out; - } - - if (info->attrs[IPOE_ATTR_PEER_ADDR]) + if (info->attrs[IPOE_ATTR_PEER_ADDR]) { peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]); + if (peer_addr) { + ses = ipoe_lookup(peer_addr); + if (ses) { + atomic_dec(&ses->refs); + return -EEXIST; + } + } + } if (info->attrs[IPOE_ATTR_ADDR]) addr = nla_get_be32(info->attrs[IPOE_ATTR_ADDR]); @@ -1020,7 +1041,20 @@ static int ipoe_nl_cmd_create(struct sk_buff *skb, struct genl_info *info) else memset(hwaddr, 0, sizeof(hwaddr)); - pr_info("ipoe: create %08x %08x %s\n", peer_addr, addr, info->attrs[IPOE_ATTR_IFNAME] ? ifname : "-"); + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, + &ipoe_nl_family, 0, IPOE_CMD_CREATE); + if (IS_ERR(hdr)) { + ret = PTR_ERR(hdr); + goto err_out; + } + + //pr_info("ipoe: create %08x %08x %s\n", peer_addr, addr, info->attrs[IPOE_ATTR_IFNAME] ? ifname : "-"); ret = ipoe_create(peer_addr, addr, info->attrs[IPOE_ATTR_IFNAME] ? ifname : NULL, hwaddr); @@ -1048,6 +1082,7 @@ out: static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; + struct ipoe_session *ses; int ifindex; int r = 0; int ret = -EINVAL; @@ -1074,7 +1109,7 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) ses = netdev_priv(dev); - pr_info("ipoe: delete %08x\n", ses->peer_addr); + //pr_info("ipoe: delete %08x\n", ses->peer_addr); if (ses->peer_addr) list_del_rcu(&ses->entry); @@ -1084,8 +1119,6 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) synchronize_rcu(); - atomic_dec(&ses->refs); - while (atomic_read(&ses->refs)) schedule_timeout_uninterruptible(1); @@ -1098,7 +1131,6 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) out_unlock: up(&ipoe_wlock); -out: return ret; } @@ -1107,15 +1139,16 @@ static int ipoe_nl_cmd_modify(struct sk_buff *skb, struct genl_info *info) int ret = -EINVAL, r = 0; char ifname[IFNAMSIZ]; struct net_device *dev, *link_dev, *old_dev; - struct ipoe_session *ses; + struct ipoe_session *ses, *ses1; int ifindex; + __be32 peer_addr; if (!info->attrs[IPOE_ATTR_IFINDEX]) - goto out; + return -EINVAL; down(&ipoe_wlock); - ifindex = nla_get_be32(info->attrs[IPOE_ATTR_IFINDEX]); + ifindex = nla_get_u32(info->attrs[IPOE_ATTR_IFINDEX]); rcu_read_lock(); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) @@ -1132,13 +1165,40 @@ static int ipoe_nl_cmd_modify(struct sk_buff *skb, struct genl_info *info) ses = netdev_priv(dev); + if (info->attrs[IPOE_ATTR_PEER_ADDR]) { + peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]); + if (peer_addr) { + ses1 = ipoe_lookup(peer_addr); + if (ses1) { + atomic_dec(&ses1->refs); + if (ses1 != ses) { + ret = -EEXIST; + goto out_unlock; + } + } + } + + if (ses->peer_addr) { + list_del_rcu(&ses->entry); + synchronize_rcu(); + } + + ses->peer_addr = peer_addr; + + if (peer_addr) + list_add_tail_rcu(&ses->entry, &ipoe_list[hash_addr(peer_addr)]); + } + if (info->attrs[IPOE_ATTR_IFNAME]) { nla_strlcpy(ifname, info->attrs[IPOE_ATTR_IFNAME], IFNAMSIZ - 1); - - link_dev = dev_get_by_name(&init_net, ifname); + + if (*ifname) { + link_dev = dev_get_by_name(&init_net, ifname); - if (!link_dev) - goto out_unlock; + if (!link_dev) + goto out_unlock; + } else + link_dev = NULL; old_dev = ses->link_dev; ses->link_dev = link_dev; @@ -1146,31 +1206,23 @@ static int ipoe_nl_cmd_modify(struct sk_buff *skb, struct genl_info *info) dev_put(old_dev); } - if (info->attrs[IPOE_ATTR_PEER_ADDR]) { - if (ses->peer_addr) { - list_del_rcu(&ses->entry); - synchronize_rcu(); - } - - ses->peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]); - - if (ses->peer_addr) - list_add_tail_rcu(&ses->entry, &ipoe_list[hash_addr(ses->peer_addr)]) - } - - if (info->attrs[IPOE_ATTR_ADDR]) + if (info->attrs[IPOE_ATTR_ADDR]) { ses->addr = nla_get_be32(info->attrs[IPOE_ATTR_ADDR]); + if (ses->addr) + dev->flags |= IFF_NOARP; + else + dev->flags &= ~IFF_NOARP; + } if (info->attrs[IPOE_ATTR_HWADDR]) nla_memcpy(ses->hwaddr, info->attrs[IPOE_ATTR_HWADDR], ETH_ALEN); - pr_info("ipoe: modify %08x %08x\n", ses->peer_addr, ses->addr); + //pr_info("ipoe: modify %08x %08x\n", ses->peer_addr, ses->addr); ret = 0; out_unlock: up(&ipoe_wlock); -out: return ret; } @@ -1400,7 +1452,7 @@ static void __exit ipoe_fini(void) rcu_barrier(); while (!list_empty(&ipoe_list2)) { - ses = list_entry(ipoe_list2.next, typeof(*ses), entry); + ses = list_entry(ipoe_list2.next, typeof(*ses), entry2); list_del(&ses->entry2); if (ses->link_dev) |