diff options
author | Kozlov Dmitry <xeb@mail.ru> | 2012-07-13 18:28:28 +0400 |
---|---|---|
committer | Kozlov Dmitry <xeb@mail.ru> | 2012-07-13 18:28:28 +0400 |
commit | dc6c5ef90626b6d475923adc1681e4684fa92933 (patch) | |
tree | 1e44a9076bb5d79b27404457d658b381971f0f4c | |
parent | 00e44c01c236a0e805add33dc983c270315dd7c7 (diff) | |
download | accel-ppp-dc6c5ef90626b6d475923adc1681e4684fa92933.tar.gz accel-ppp-dc6c5ef90626b6d475923adc1681e4684fa92933.zip |
ipoe: shared nat support
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 32 | ||||
-rw-r--r-- | drivers/ipoe/ipoe.c | 100 |
2 files changed, 113 insertions, 19 deletions
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index b961ef8..ae4dca2 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -247,25 +247,27 @@ static void ipoe_session_start(struct ipoe_session *ses) } ses->ses.ipv4 = ipdb_get_ipv4(&ses->ses); - if (!ses->ses.ipv4) { + /*if (!ses->ses.ipv4) { log_ppp_warn("no free IPv4 address\n"); ap_session_terminate(&ses->ses, TERM_AUTH_ERROR, 0); return; - } + }*/ - if (conf_gw_address) - ses->ses.ipv4->addr = conf_gw_address; - - if (conf_netmask) - ses->ses.ipv4->mask = conf_netmask; - else if (!ses->ses.ipv4->mask) - ses->ses.ipv4->mask = 24; + if (ses->ses.ipv4) { + if (conf_gw_address) + ses->ses.ipv4->addr = conf_gw_address; + + if (conf_netmask) + ses->ses.ipv4->mask = conf_netmask; + else if (!ses->ses.ipv4->mask) + ses->ses.ipv4->mask = 24; - if (!ses->yiaddr) - ses->yiaddr = ses->ses.ipv4->peer_addr; - - if (!ses->siaddr) - ses->siaddr = ses->ses.ipv4->addr; + if (!ses->yiaddr) + ses->yiaddr = ses->ses.ipv4->peer_addr; + + if (!ses->siaddr) + ses->siaddr = ses->ses.ipv4->addr; + } if (!ses->mask) ses->mask = 24; @@ -280,7 +282,7 @@ static void ipoe_session_start(struct ipoe_session *ses) 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->yiaddr, ses->ses.ipv4->peer_addr, NULL, NULL)) + if (ipoe_nl_modify(ses->ifindex, ses->yiaddr, ses->ses.ipv4 ? ses->ses.ipv4->peer_addr : 1, NULL, NULL)) ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 0); else ap_session_activate(&ses->ses); diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 2d500ef..1879da8 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -6,6 +6,7 @@ #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/inetdevice.h> #include <linux/in.h> #include <linux/tcp.h> #include <linux/udp.h> @@ -14,6 +15,7 @@ #include <linux/init.h> #include <linux/if_ether.h> #include <linux/semaphore.h> +#include <linux/netfilter_ipv4.h> #include <linux/version.h> #include <net/genetlink.h> @@ -61,6 +63,7 @@ struct ipoe_session __be32 addr; __be32 peer_addr; + __be32 l4_redirect; __u8 hwaddr[ETH_ALEN]; struct net_device *dev; @@ -336,6 +339,7 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev) struct iphdr *iph; struct ethhdr *eth; struct sk_buff *skb1; + struct dst_entry *dst; /*struct arphdr *arp; unsigned char *arp_ptr; __be32 tip;*/ @@ -361,6 +365,18 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_bytes += skb->len - ETH_HLEN; #endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) + dst = skb_dst(skb); +#else + dst = skb->dst; +#endif + if (dst && dst->dev != skb->dev) { + skb->dev = dst->dev; + dev_queue_xmit(skb); + return NETDEV_TX_OK; + } + if (iph->daddr == ses->addr) { if (skb_shared(skb)) { skb1 = skb_clone(skb, GFP_ATOMIC); @@ -485,7 +501,7 @@ static int ipoe_rcv_arp(struct sk_buff *skb, struct net_device *dev, struct pack } 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; @@ -544,6 +560,7 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t if (!ses) { ipoe_queue_u(skb, iph->saddr); + skb->pkt_type = PACKET_OTHERHOST; goto drop; } @@ -574,7 +591,7 @@ 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)) { + if (ses->addr > 1 && ipoe_do_nat(skb1, ses->addr, 0)) { kfree_skb(skb1); goto drop_unlock; } @@ -790,6 +807,38 @@ static struct ipoe_session *ipoe_lookup(__be32 addr) return NULL; } +static unsigned int ipt_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *skb)) +{ + int noff; + struct iphdr *iph; + struct ipoe_session *ses; + + if (skb->protocol != htons(ETH_P_IP)) + return NF_ACCEPT; + + noff = skb_network_offset(skb); + + if (!pskb_may_pull(skb, sizeof(*iph) + noff)) + return NF_ACCEPT; + + iph = ip_hdr(skb); + + if (!ipoe_check_network(iph->daddr)) + return NF_ACCEPT; + + if (ipoe_check_network(iph->saddr)) + return NF_ACCEPT; + + ses = ipoe_lookup(iph->daddr); + if (!ses) + return NF_ACCEPT; + + skb->dev = ses->dev; + atomic_dec(&ses->refs); + + return NF_ACCEPT; +} + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) static struct rtnl_link_stats64 *ipoe_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) @@ -901,6 +950,7 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, c char name[IFNAMSIZ]; int r = -EINVAL; int h = hash_addr(peer_addr); + struct in_device *in_dev; if (link_ifname) { link_dev = dev_get_by_name(&init_net, link_ifname); @@ -950,6 +1000,14 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, c dev->flags |= IFF_NOARP; else dev->flags &= ~IFF_NOARP; + + in_dev = __in_dev_get_rtnl(dev); + if (in_dev) { + if (addr == 1) + IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; + else + IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 1; + } rtnl_lock(); r = register_netdevice(dev); @@ -1139,6 +1197,7 @@ 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 in_device *in_dev; struct ipoe_session *ses, *ses1; int ifindex; __be32 peer_addr; @@ -1219,6 +1278,14 @@ static int ipoe_nl_cmd_modify(struct sk_buff *skb, struct genl_info *info) dev->flags |= IFF_NOARP; else dev->flags &= ~IFF_NOARP; + + in_dev = __in_dev_get_rtnl(dev); + if (in_dev) { + if (ses->addr == 1) + IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; + else + IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 1; + } } if (info->attrs[IPOE_ATTR_HWADDR]) @@ -1414,6 +1481,23 @@ static struct packet_type arp_packet_type = { .func = ipoe_rcv_arp, }; +static struct nf_hook_ops ipt_ops[] __read_mostly = { + { + .hook = ipt_hook, + .pf = PF_INET, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_LAST, + .owner = THIS_MODULE, + }, + { + .hook = ipt_hook, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_LAST, + .owner = THIS_MODULE, + }, +}; + /*static struct pernet_operations ipoe_net_ops = { .init = ipoe_init_net, .exit = ipoe_exit_net, @@ -1434,6 +1518,9 @@ static int __init ipoe_init(void) INIT_LIST_HEAD(&ipoe_list[i]); INIT_LIST_HEAD(&ipoe_list1_u[i]); } + + skb_queue_head_init(&ipoe_queue); + INIT_WORK(&ipoe_queue_work, ipoe_process_queue); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) err = genl_register_family(&ipoe_nl_family); @@ -1467,8 +1554,11 @@ static int __init ipoe_init(void) goto out_unreg; } - skb_queue_head_init(&ipoe_queue); - INIT_WORK(&ipoe_queue_work, ipoe_process_queue); + err = nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); + if (err < 0) { + printk(KERN_INFO "ipoe: can't register nf hooks\n"); + goto out_unreg; + } dev_add_pack(&ip_packet_type); dev_add_pack(&arp_packet_type); @@ -1491,6 +1581,8 @@ static void __exit ipoe_fini(void) genl_unregister_mc_group(&ipoe_nl_family, &ipoe_nl_mcg); genl_unregister_family(&ipoe_nl_family); + nf_unregister_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); + dev_remove_pack(&ip_packet_type); dev_remove_pack(&arp_packet_type); |