static unsigned int route6_oif(const struct ip6t_route_target_info *route_info, struct sk_buff *skb) { unsigned int ifindex = 0; struct net_device *dev_out = NULL; /* The user set the interface name to use. * Getting the current interface index. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) if ((dev_out = dev_get_by_name(&init_net, route_info->oif))) { #else if ((dev_out = dev_get_by_name(route_info->oif))) { #endif ifindex = dev_out->ifindex; } else { /* Unknown interface name : packet dropped */ if (net_ratelimit()) DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); if (route_info->flags & IP6T_ROUTE_CONTINUE) return IP6T_CONTINUE; else return NF_DROP; } /* Trying the standard way of routing packets */ if (route6(skb, ifindex, route_info)) { dev_put(dev_out); if (route_info->flags & IP6T_ROUTE_CONTINUE) return IP6T_CONTINUE; ip_direct_send(skb); return NF_STOLEN; } else return NF_DROP; } static unsigned int route6_gw(const struct ip6t_route_target_info *route_info, struct sk_buff *skb) { if (route6(skb, 0, route_info)) { if (route_info->flags & IP6T_ROUTE_CONTINUE) return IP6T_CONTINUE; ip_direct_send(skb); return NF_STOLEN; } else return NF_DROP; } static unsigned int #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) target(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, const void *targinfo, void *userinfo) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const void *targinfo, void *userinfo) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo, void *userinfo) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) target(struct sk_buff *skb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) target(struct sk_buff *skb, const struct xt_target_param *par) #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) */ target(struct sk_buff *skb, const struct xt_action_param *par) #endif { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) const struct ip6t_route_target_info *route_info = targinfo; #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) const struct ip6t_route_target_info *route_info = par->targinfo; unsigned int hooknum = par->hooknum; #else const struct ip6t_route_target_info *route_info = par->targinfo; unsigned int hooknum = par->hooknum; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) struct sk_buff *skb = *pskb; #endif struct in6_addr *gw = (struct in6_addr*)&route_info->gw; unsigned int res; if (route_info->flags & IP6T_ROUTE_CONTINUE) goto do_it; /* If we are at PREROUTING or INPUT hook * the TTL isn't decreased by the IP stack */ if (hooknum == NF_INET_PRE_ROUTING || hooknum == NF_INET_LOCAL_IN) { struct ipv6hdr *ipv6h = ipv6_hdr(skb); if (ipv6h->hop_limit <= 1) { /* Force OUTPUT device used as source address */ skb->dev = skb_dst(skb)->dev; icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0); return NF_DROP; } ipv6h->hop_limit--; } if ((route_info->flags & IP6T_ROUTE_TEE)) { /* * Copy the skb, and route the copy. Will later return * IP6T_CONTINUE for the original skb, which should continue * on its way as if nothing happened. The copy should be * independantly delivered to the ROUTE --gw. */ skb = skb_copy(skb, GFP_ATOMIC); if (!skb) { if (net_ratelimit()) DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); return IP6T_CONTINUE; } } do_it: if (route_info->oif[0]) { res = route6_oif(route_info, skb); } else if (!ipv6_addr_any(gw)) { res = route6_gw(route_info, skb); } else { if (net_ratelimit()) DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); res = IP6T_CONTINUE; } if ((route_info->flags & IP6T_ROUTE_TEE)) res = IP6T_CONTINUE; return res; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) static int checkentry(const char *tablename, const struct ip6t_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) static int checkentry(const char *tablename, const void *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) static int checkentry(const char *tablename, const void *e, const struct xt_target *target, void *targinfo, unsigned int targinfosize, unsigned int hook_mask) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) static int checkentry(const char *tablename, const void *e, const struct xt_target *target, void *targinfo, unsigned int hook_mask) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) static bool checkentry(const char *tablename, const void *e, const struct xt_target *target, void *targinfo, unsigned int hook_mask) #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */ static bool checkentry(const struct xt_tgchk_param *par) #endif { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) const char *tablename = par->table; #endif if (strcmp(tablename, "mangle") != 0) { printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", targinfosize, IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); return 0; } #endif return 1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) static struct xt_target ip6t_route_reg = { #else static struct ip6t_target ip6t_route_reg = { #endif .name = "ROUTE", #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) .family = AF_INET6, #endif .target = target, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) .targetsize = sizeof(struct ip6t_route_target_info), #endif .checkentry = checkentry, .me = THIS_MODULE }; static int __init init(void) { printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) if (xt_register_target(&ip6t_route_reg)) #else if (ip6t_register_target(&ip6t_route_reg)) #endif return -EINVAL; return 0; } static void __exit fini(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) xt_unregister_target(&ip6t_route_reg); #else ip6t_unregister_target(&ip6t_route_reg); #endif } module_init(init); module_exit(fini); MODULE_LICENSE("GPL");
static unsigned int ip6t_route_target(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, const void *targinfo, void *userinfo) { const struct ip6t_route_target_info *route_info = targinfo; struct sk_buff *skb = *pskb; struct in6_addr *gw = (struct in6_addr*)&route_info->gw; unsigned int res; if (route_info->flags & IP6T_ROUTE_CONTINUE) goto do_it; /* If we are at PREROUTING or INPUT hook * the TTL isn't decreased by the IP stack */ if (hooknum == NF_IP6_PRE_ROUTING || hooknum == NF_IP6_LOCAL_IN) { struct ipv6hdr *ipv6h = skb->nh.ipv6h; if (ipv6h->hop_limit <= 1) { /* Force OUTPUT device used as source address */ skb->dev = skb->dst->dev; icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0, skb->dev); return NF_DROP; } ipv6h->hop_limit--; } if ((route_info->flags & IP6T_ROUTE_TEE)) { /* * Copy the *pskb, and route the copy. Will later return * IP6T_CONTINUE for the original skb, which should continue * on its way as if nothing happened. The copy should be * independantly delivered to the ROUTE --gw. */ skb = skb_copy(*pskb, GFP_ATOMIC); if (!skb) { if (net_ratelimit()) DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); return IP6T_CONTINUE; } } do_it: if (route_info->oif[0]) { res = route6_oif(route_info, skb); } else if (!ipv6_addr_any(gw)) { res = route6_gw(route_info, skb); } else { if (net_ratelimit()) DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); res = IP6T_CONTINUE; } if ((route_info->flags & IP6T_ROUTE_TEE)) res = IP6T_CONTINUE; return res; }