Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #include <linux/skbuff.h>
0004 #include <linux/netfilter.h>
0005 #include <linux/netfilter_ipv4.h>
0006 #include <linux/netfilter_ipv6.h>
0007 #include <linux/netfilter/nfnetlink.h>
0008 #include <linux/netfilter/nf_tables.h>
0009 #include <net/netfilter/nf_tables.h>
0010 #include <net/netfilter/nf_tables_ipv4.h>
0011 #include <net/netfilter/nf_tables_ipv6.h>
0012 #include <net/route.h>
0013 #include <net/ip.h>
0014 
0015 #ifdef CONFIG_NF_TABLES_IPV4
0016 static unsigned int nf_route_table_hook4(void *priv,
0017                      struct sk_buff *skb,
0018                      const struct nf_hook_state *state)
0019 {
0020     const struct iphdr *iph;
0021     struct nft_pktinfo pkt;
0022     __be32 saddr, daddr;
0023     unsigned int ret;
0024     u32 mark;
0025     int err;
0026     u8 tos;
0027 
0028     nft_set_pktinfo(&pkt, skb, state);
0029     nft_set_pktinfo_ipv4(&pkt);
0030 
0031     mark = skb->mark;
0032     iph = ip_hdr(skb);
0033     saddr = iph->saddr;
0034     daddr = iph->daddr;
0035     tos = iph->tos;
0036 
0037     ret = nft_do_chain(&pkt, priv);
0038     if (ret == NF_ACCEPT) {
0039         iph = ip_hdr(skb);
0040 
0041         if (iph->saddr != saddr ||
0042             iph->daddr != daddr ||
0043             skb->mark != mark ||
0044             iph->tos != tos) {
0045             err = ip_route_me_harder(state->net, state->sk, skb, RTN_UNSPEC);
0046             if (err < 0)
0047                 ret = NF_DROP_ERR(err);
0048         }
0049     }
0050     return ret;
0051 }
0052 
0053 static const struct nft_chain_type nft_chain_route_ipv4 = {
0054     .name       = "route",
0055     .type       = NFT_CHAIN_T_ROUTE,
0056     .family     = NFPROTO_IPV4,
0057     .hook_mask  = (1 << NF_INET_LOCAL_OUT),
0058     .hooks      = {
0059         [NF_INET_LOCAL_OUT] = nf_route_table_hook4,
0060     },
0061 };
0062 #endif
0063 
0064 #ifdef CONFIG_NF_TABLES_IPV6
0065 static unsigned int nf_route_table_hook6(void *priv,
0066                      struct sk_buff *skb,
0067                      const struct nf_hook_state *state)
0068 {
0069     struct in6_addr saddr, daddr;
0070     struct nft_pktinfo pkt;
0071     u32 mark, flowlabel;
0072     unsigned int ret;
0073     u8 hop_limit;
0074     int err;
0075 
0076     nft_set_pktinfo(&pkt, skb, state);
0077     nft_set_pktinfo_ipv6(&pkt);
0078 
0079     /* save source/dest address, mark, hoplimit, flowlabel, priority */
0080     memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr));
0081     memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr));
0082     mark = skb->mark;
0083     hop_limit = ipv6_hdr(skb)->hop_limit;
0084 
0085     /* flowlabel and prio (includes version, which shouldn't change either)*/
0086     flowlabel = *((u32 *)ipv6_hdr(skb));
0087 
0088     ret = nft_do_chain(&pkt, priv);
0089     if (ret == NF_ACCEPT &&
0090         (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) ||
0091          memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) ||
0092          skb->mark != mark ||
0093          ipv6_hdr(skb)->hop_limit != hop_limit ||
0094          flowlabel != *((u32 *)ipv6_hdr(skb)))) {
0095         err = nf_ip6_route_me_harder(state->net, state->sk, skb);
0096         if (err < 0)
0097             ret = NF_DROP_ERR(err);
0098     }
0099 
0100     return ret;
0101 }
0102 
0103 static const struct nft_chain_type nft_chain_route_ipv6 = {
0104     .name       = "route",
0105     .type       = NFT_CHAIN_T_ROUTE,
0106     .family     = NFPROTO_IPV6,
0107     .hook_mask  = (1 << NF_INET_LOCAL_OUT),
0108     .hooks      = {
0109         [NF_INET_LOCAL_OUT] = nf_route_table_hook6,
0110     },
0111 };
0112 #endif
0113 
0114 #ifdef CONFIG_NF_TABLES_INET
0115 static unsigned int nf_route_table_inet(void *priv,
0116                     struct sk_buff *skb,
0117                     const struct nf_hook_state *state)
0118 {
0119     struct nft_pktinfo pkt;
0120 
0121     switch (state->pf) {
0122     case NFPROTO_IPV4:
0123         return nf_route_table_hook4(priv, skb, state);
0124     case NFPROTO_IPV6:
0125         return nf_route_table_hook6(priv, skb, state);
0126     default:
0127         nft_set_pktinfo(&pkt, skb, state);
0128         break;
0129     }
0130 
0131     return nft_do_chain(&pkt, priv);
0132 }
0133 
0134 static const struct nft_chain_type nft_chain_route_inet = {
0135     .name       = "route",
0136     .type       = NFT_CHAIN_T_ROUTE,
0137     .family     = NFPROTO_INET,
0138     .hook_mask  = (1 << NF_INET_LOCAL_OUT),
0139     .hooks      = {
0140         [NF_INET_LOCAL_OUT] = nf_route_table_inet,
0141     },
0142 };
0143 #endif
0144 
0145 void __init nft_chain_route_init(void)
0146 {
0147 #ifdef CONFIG_NF_TABLES_IPV6
0148     nft_register_chain_type(&nft_chain_route_ipv6);
0149 #endif
0150 #ifdef CONFIG_NF_TABLES_IPV4
0151     nft_register_chain_type(&nft_chain_route_ipv4);
0152 #endif
0153 #ifdef CONFIG_NF_TABLES_INET
0154     nft_register_chain_type(&nft_chain_route_inet);
0155 #endif
0156 }
0157 
0158 void __exit nft_chain_route_fini(void)
0159 {
0160 #ifdef CONFIG_NF_TABLES_IPV6
0161     nft_unregister_chain_type(&nft_chain_route_ipv6);
0162 #endif
0163 #ifdef CONFIG_NF_TABLES_IPV4
0164     nft_unregister_chain_type(&nft_chain_route_ipv4);
0165 #endif
0166 #ifdef CONFIG_NF_TABLES_INET
0167     nft_unregister_chain_type(&nft_chain_route_inet);
0168 #endif
0169 }