注册 登录
自由的生活_软路由 返回首页

心想事成的个人空间 https://bbs.routerclub.com/?681 [收藏] [复制] [分享] [RSS]

日志

linux内核iptroute模块

已有 1492 次阅读2013-3-17 15:44 | linux, 模块

This kernel module implements the ROUTE target for iptables, which enables you to setup unusual routes not supported by the standard kernel routing table.

For now ipt_ROUTE.c tested with kernels from 2.6.26 to 2.6.28.


http://code.google.com/p/iptroute/



/*
 * This implements the ROUTE target, which enables you to setup unusual
 * routes not supported by the standard kernel routing table.
 *
 * Copyright (C) 2002 Cedric de Launois <delaunois@info.ucl.ac.be>
 * Fixed to compile with kernels >=2.6.24 by m0sia (m0sia@m0sia.ru)
 *
 * v 1.12 2009/03/20
 * This software is distributed under GNU GPL v2, 1991
 */


#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include "ipt_ROUTE.h"
#include <linux/netdevice.h>
#include <linux/route.h>
#include <linux/version.h>
#include <linux/if_arp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/icmp.h>
#include <net/checksum.h>

#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cedric de Launois <delaunois@info.ucl.ac.be>");
MODULE_DESCRIPTION("iptables ROUTE target module");

#define NF_IP_PRE_ROUTING       0
#define NF_IP_LOCAL_IN          1
#define NF_IP_FORWARD           2
#define NF_IP_LOCAL_OUT         3
#define NF_IP_POST_ROUTING      4
#define NF_IP_NUMHOOKS          5

/* Try to route the packet according to the routing keys specified in
 * route_info. Keys are :
 *  - ifindex :
 *      0 if no oif preferred,
 *      otherwise set to the index of the desired oif
 *  - route_info->gw :
 *      0 if no gateway specified,
 *      otherwise set to the next host to which the pkt must be routed
 * If success, skb->dev is the output device to which the packet must
 * be sent and skb->dst is not NULL
 *
 * RETURN: -1 if an error occured
 *          1 if the packet was succesfully routed to the
 *            destination desired
 *          0 if the kernel routing table could not route the packet
 *            according to the keys specified
 */
static int route(struct sk_buff *skb,
                 unsigned int ifindex,
                 const struct ipt_route_target_info *route_info)
{
        int err;
        struct rtable *rt;
        struct iphdr *iph = ip_hdr(skb);
        struct flowi fl = {
                .oif = ifindex,
                .nl_u = {
                        .ip4_u = {
                                .daddr = iph->daddr,
                                .saddr = 0,
                                .tos = RT_TOS(iph->tos),
                                .scope = RT_SCOPE_UNIVERSE,
                        }
                }
        };
       
        /* The destination address may be overloaded by the target */
        if (route_info->gw)
                fl.fl4_dst = route_info->gw;
       
        /* Trying to route the packet using the standard routing table. */
        if ((err = ip_route_output_key(&init_net,&rt, &fl))) {
                if (net_ratelimit())
                        DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err);
                return -1;
        }
       
        /* Drop old route. */
        dst_release(skb->dst);
        skb->dst = NULL;

        /* Success if no oif specified or if the oif correspond to the
         * one desired */
        if (!ifindex || rt->u.dst.dev->ifindex == ifindex) {
                skb->dst = &rt->u.dst;
                skb->dev = skb->dst->dev;
                skb->protocol = htons(ETH_P_IP);
                return 1;
        }
       
        /* The interface selected by the routing table is not the one
         * specified by the user. This may happen because the dst address
         * is one of our own addresses.
         */
        if (net_ratelimit())
                DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n",
                       NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex);
       
        return 0;
}


/* Stolen from ip_finish_output2
 * PRE : skb->dev is set to the device we are leaving by
 *       skb->dst is not NULL
 * POST: the packet is sent with the link layer header pushed
 *       the packet is destroyed
 */
static void ip_direct_send(struct sk_buff *skb)
{
        struct dst_entry *dst = skb->dst;
        struct hh_cache *hh = dst->hh;
        struct net_device *dev = dst->dev;
        int hh_len = LL_RESERVED_SPACE(dev);

        /* Be paranoid, rather than too clever. */
        if (unlikely(skb_headroom(skb) < hh_len )) {
                struct sk_buff *skb2;

                skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
                if (skb2 == NULL) {
                        kfree_skb(skb);
                        return;
                }
                if (skb->sk)
                        skb_set_owner_w(skb2, skb->sk);
                kfree_skb(skb);
                skb = skb2;
        }

        if (hh) {
                int hh_alen;

                write_seqlock_bh(&hh->hh_lock);
                hh_alen = HH_DATA_ALIGN(hh->hh_len);
                memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
                write_sequnlock_bh(&hh->hh_lock);
                skb_push(skb, hh->hh_len);
                hh->hh_output(skb);
        } else if (dst->neighbour)
                dst->neighbour->output(skb);
        else {
                if (net_ratelimit())
                        DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n");
                kfree_skb(skb);
        }
}


/* PRE : skb->dev is set to the device we are leaving by
 * POST: - the packet is directly sent to the skb->dev device, without
 *         pushing the link layer header.
 *       - the packet is destroyed
 */
static inline int dev_direct_send(struct sk_buff *skb)
{
        return dev_queue_xmit(skb);
}


static unsigned int route_oif(const struct ipt_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 ((dev_out = dev_get_by_name(&init_net,route_info->oif))) {
                ifindex = dev_out->ifindex;
        } else {
                /* Unknown interface name : packet dropped */
                if (net_ratelimit())
                        DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif);
                return NF_DROP;
        }

        /* Trying the standard way of routing packets */
        switch (route(skb, ifindex, route_info)) {
        case 1:
                dev_put(dev_out);
                if (route_info->flags & IPT_ROUTE_CONTINUE)
                        return IPT_CONTINUE;

                ip_direct_send(skb);
                return NF_STOLEN;

        case 0:
                /* Failed to send to oif. Trying the hard way */
                if (route_info->flags & IPT_ROUTE_CONTINUE)
                        return NF_DROP;

                if (net_ratelimit())
                        DEBUGP("ipt_ROUTE: forcing the use of %i\n",
                               ifindex);

                /* We have to force the use of an interface.
                 * This interface must be a tunnel interface since
                 * otherwise we can't guess the hw address for
                 * the packet. For a tunnel interface, no hw address
                 * is needed.
                 */
                if ((dev_out->type != ARPHRD_TUNNEL)
                    && (dev_out->type != ARPHRD_IPGRE)) {
                        if (net_ratelimit())
                                DEBUGP("ipt_ROUTE: can't guess the hw addr !\n");
                        dev_put(dev_out);
                        return NF_DROP;
                }
       
                /* Send the packet. This will also free skb
                 * Do not go through the POST_ROUTING hook because
                 * skb->dst is not set and because it will probably
                 * get confused by the destination IP address.
                 */
                skb->dev = dev_out;
                dev_direct_send(skb);
                dev_put(dev_out);
                return NF_STOLEN;
               
        default:
                /* Unexpected error */
                dev_put(dev_out);
                return NF_DROP;
        }
}


static unsigned int route_iif(const struct ipt_route_target_info *route_info,
                              struct sk_buff *skb)
{
        struct net_device *dev_in = NULL;

        /* Getting the current interface index. */
        if (!(dev_in = dev_get_by_name(&init_net,route_info->iif))) {
                if (net_ratelimit())
                        DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->iif);
                return NF_DROP;
        }

        skb->dev = dev_in;
        dst_release(skb->dst);
        skb->dst = NULL;

        netif_rx(skb);
        dev_put(dev_in);
        return NF_STOLEN;
}


static unsigned int route_gw(const struct ipt_route_target_info *route_info,
                             struct sk_buff *skb)
{
        if (route(skb, 0, route_info)!=1)
                return NF_DROP;

        if (route_info->flags & IPT_ROUTE_CONTINUE)
                return IPT_CONTINUE;

        ip_direct_send(skb);
        return NF_STOLEN;
}


/* To detect and deter routed packet loopback when using the --tee option,
 * we take a page out of the raw.patch book: on the copied skb, we set up
 * a fake ->nfct entry, pointing to the local &route_tee_track. We skip
 * routing packets when we see they already have that ->nfct.
 */

static struct nf_conn route_tee_track;

static unsigned int ipt_route_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)
{
        const struct ipt_route_target_info *route_info = targinfo;
        //struct sk_buff *skb = *pskb;
        unsigned int res;

        if (skb->nfct == &route_tee_track.ct_general) {
                /* Loopback - a packet we already routed, is to be
                 * routed another time. Avoid that, now.
                 */
                if (net_ratelimit())
                        DEBUGP(KERN_DEBUG "ipt_ROUTE: loopback - DROP!\n");
                return NF_DROP;
        }

        /* If we are at PREROUTING or INPUT hook
         * the TTL isn't decreased by the IP stack
         */
        if (hooknum == NF_IP_PRE_ROUTING ||
            hooknum == NF_IP_LOCAL_IN) {

                struct iphdr *iph = ip_hdr(skb);

                if (iph->ttl <= 1) {
                        struct rtable *rt;
                        struct flowi fl = {
                                .oif = 0,
                                .nl_u = {
                                        .ip4_u = {
                                                .daddr = iph->daddr,
                                                .saddr = iph->saddr,
                                                .tos = RT_TOS(iph->tos),
                                                .scope = ((iph->tos & RTO_ONLINK) ?
                                                          RT_SCOPE_LINK :
                                                          RT_SCOPE_UNIVERSE)
                                        }
                                }
                        };

                        if (ip_route_output_key(&init_net,&rt, &fl)) {
                                return NF_DROP;
                        }

                        if (skb->dev == rt->u.dst.dev) {
                                /* Drop old route. */
                                dst_release(skb->dst);
                                skb->dst = &rt->u.dst;

                                /* this will traverse normal stack, and
                                 * thus call conntrack on the icmp packet */
                                icmp_send(skb, ICMP_TIME_EXCEEDED,
                                          ICMP_EXC_TTL, 0);
                        }

                        return NF_DROP;
                }

                /*
                 * If we are at INPUT the checksum must be recalculated since
                 * the length could change as the result of a defragmentation.
                 */
                if(hooknum == NF_IP_LOCAL_IN) {
                        iph->ttl = iph->ttl - 1;
                        iph->check = 0;
                        iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
                } else {
                        ip_decrease_ttl(iph);
                }
        }

        if ((route_info->flags & IPT_ROUTE_TEE)) {
                /*
                 * Copy the *pskb, and route the copy. Will later return
                 * IPT_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 "ipt_ROUTE: copy failed!\n");
                        return IPT_CONTINUE;
                }
        }

        /* Tell conntrack to forget this packet since it may get confused
         * when a packet is leaving with dst address == our address.
         * Good idea ? Dunno. Need advice.
         *
         * NEW: mark the skb with our &route_tee_track, so we avoid looping
         * on any already routed packet.
         */
        if (!(route_info->flags & IPT_ROUTE_CONTINUE)) {
                nf_conntrack_put(skb->nfct);
                skb->nfct = &route_tee_track.ct_general;
                skb->nfctinfo = IP_CT_NEW;
                nf_conntrack_get(skb->nfct);
        }

        if (route_info->oif[0] != '\0') {
                res = route_oif(route_info, skb);
        } else if (route_info->iif[0] != '\0') {
                res = route_iif(route_info, skb);
        } else if (route_info->gw) {
                res = route_gw(route_info, skb);
        } else {
                if (net_ratelimit())
                        DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n");
                res = IPT_CONTINUE;
        }

        if ((route_info->flags & IPT_ROUTE_TEE))
                res = IPT_CONTINUE;

        return res;
}
static bool ipt_route_checkentry(const char *tablename,
                 const void *e,
                 const struct xt_target *target,
                 void *targinfo,
                 unsigned int hook_mask)
{
        return 1;
}


static struct ipt_target ipt_route_reg = {
        .name = "ROUTE",
        .family = AF_INET,
        .target = ipt_route_target,
        .targetsize = sizeof(struct ipt_route_target_info),
        .checkentry = ipt_route_checkentry,
        .table  = "mangle",
        .hooks  = 1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN | 1 << NF_IP_FORWARD | 1 << NF_IP_LOCAL_OUT | 1 << NF_IP_POST_ROUTING,
        .me = THIS_MODULE,
};

static int __init init(void)
{
        /* Set up fake conntrack (stolen from raw.patch):
            - to never be deleted, not in any hashes */
        atomic_set(&route_tee_track.ct_general.use, 1);
        /*  - and look it like as a confirmed connection */
        set_bit(IPS_CONFIRMED_BIT, &route_tee_track.status);
        /* Initialize fake conntrack so that NAT will skip it */
        route_tee_track.status |= IPS_NAT_DONE_MASK;

        return xt_register_target(&ipt_route_reg);
}


static void __exit fini(void)
{
        xt_unregister_target(&ipt_route_reg);
}

module_init(init);
module_exit(fini);
/* Header file for iptables ipt_ROUTE target
 *
 * (C) 2002 by Cédric de Launois <delaunois@info.ucl.ac.be>
 *
 * This software is distributed under GNU GPL v2, 1991
 */
#ifndef _IPT_ROUTE_H_target
#define _IPT_ROUTE_H_target

#define IPT_ROUTE_IFNAMSIZ 16

struct ipt_route_target_info {
        char      oif[IPT_ROUTE_IFNAMSIZ];      /* Output Interface Name */
        char      iif[IPT_ROUTE_IFNAMSIZ];      /* Input Interface Name  */
        u_int32_t gw;                           /* IP address of gateway */
        u_int8_t  flags;
};

/* Values for "flags" field */
#define IPT_ROUTE_CONTINUE        0x01
#define IPT_ROUTE_TEE             0x02

#endif /*_IPT_ROUTE_H_target*/

路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|Archiver|手机版|小黑屋|软路由 ( 渝ICP备15001194号-1|渝公网安备 50011602500124号 )

GMT+8, 2024-5-10 11:11 , Processed in 0.074788 second(s), 5 queries , Gzip On, Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2023 Discuz! Team.

返回顶部