summaryrefslogtreecommitdiff
path: root/drivers/ipoe/ipoe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ipoe/ipoe.c')
-rw-r--r--drivers/ipoe/ipoe.c180
1 files changed, 116 insertions, 64 deletions
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)