1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 */ 6 7 #include <linux/kernel.h> 8 #include <linux/init.h> 9 #include <linux/module.h> 10 #include <linux/netlink.h> 11 #include <linux/netfilter.h> 12 #include <linux/netfilter/nf_tables.h> 13 #include <net/netfilter/nf_tables_core.h> 14 #include <net/netfilter/nf_tables.h> 15 #include <net/netfilter/nft_fib.h> 16 17 #include <net/ip_fib.h> 18 #include <net/route.h> 19 20 /* don't try to find route from mcast/bcast/zeronet */ 21 static __be32 get_saddr(__be32 addr) 22 { 23 if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) || 24 ipv4_is_zeronet(addr)) 25 return 0; 26 return addr; 27 } 28 29 static bool fib4_is_local(const struct sk_buff *skb) 30 { 31 const struct rtable *rt = skb_rtable(skb); 32 33 return rt && (rt->rt_flags & RTCF_LOCAL); 34 } 35 36 #define DSCP_BITS 0xfc 37 38 void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, 39 const struct nft_pktinfo *pkt) 40 { 41 const struct nft_fib *priv = nft_expr_priv(expr); 42 u32 *dst = ®s->data[priv->dreg]; 43 const struct net_device *dev = NULL; 44 const struct iphdr *iph; 45 __be32 addr; 46 47 if (priv->flags & NFTA_FIB_F_IIF) 48 dev = nft_in(pkt); 49 else if (priv->flags & NFTA_FIB_F_OIF) 50 dev = nft_out(pkt); 51 52 iph = ip_hdr(pkt->skb); 53 if (priv->flags & NFTA_FIB_F_DADDR) 54 addr = iph->daddr; 55 else 56 addr = iph->saddr; 57 58 *dst = inet_dev_addr_type(nft_net(pkt), dev, addr); 59 } 60 EXPORT_SYMBOL_GPL(nft_fib4_eval_type); 61 62 static int get_ifindex(const struct net_device *dev) 63 { 64 return dev ? dev->ifindex : 0; 65 } 66 67 void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, 68 const struct nft_pktinfo *pkt) 69 { 70 const struct nft_fib *priv = nft_expr_priv(expr); 71 u32 *dest = ®s->data[priv->dreg]; 72 const struct iphdr *iph; 73 struct fib_result res; 74 struct flowi4 fl4 = { 75 .flowi4_scope = RT_SCOPE_UNIVERSE, 76 .flowi4_iif = LOOPBACK_IFINDEX, 77 }; 78 const struct net_device *oif; 79 struct net_device *found; 80 #ifdef CONFIG_IP_ROUTE_MULTIPATH 81 int i; 82 #endif 83 84 /* 85 * Do not set flowi4_oif, it restricts results (for example, asking 86 * for oif 3 will get RTN_UNICAST result even if the daddr exits 87 * on another interface. 88 * 89 * Search results for the desired outinterface instead. 90 */ 91 if (priv->flags & NFTA_FIB_F_OIF) 92 oif = nft_out(pkt); 93 else if (priv->flags & NFTA_FIB_F_IIF) 94 oif = nft_in(pkt); 95 else 96 oif = NULL; 97 98 if (nft_hook(pkt) == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) { 99 nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); 100 return; 101 } 102 103 iph = ip_hdr(pkt->skb); 104 if (ipv4_is_multicast(iph->daddr) && 105 ipv4_is_zeronet(iph->saddr) && 106 ipv4_is_local_multicast(iph->daddr)) { 107 nft_fib_store_result(dest, priv->result, pkt, 108 get_ifindex(pkt->skb->dev)); 109 return; 110 } 111 112 if (priv->flags & NFTA_FIB_F_MARK) 113 fl4.flowi4_mark = pkt->skb->mark; 114 115 fl4.flowi4_tos = iph->tos & DSCP_BITS; 116 117 if (priv->flags & NFTA_FIB_F_DADDR) { 118 fl4.daddr = iph->daddr; 119 fl4.saddr = get_saddr(iph->saddr); 120 } else { 121 fl4.daddr = iph->saddr; 122 fl4.saddr = get_saddr(iph->daddr); 123 } 124 125 if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) 126 return; 127 128 switch (res.type) { 129 case RTN_UNICAST: 130 break; 131 case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */ 132 return; 133 default: 134 break; 135 } 136 137 if (!oif) { 138 found = FIB_RES_DEV(res); 139 goto ok; 140 } 141 142 #ifdef CONFIG_IP_ROUTE_MULTIPATH 143 for (i = 0; i < res.fi->fib_nhs; i++) { 144 struct fib_nh *nh = &res.fi->fib_nh[i]; 145 146 if (nh->nh_dev == oif) { 147 found = nh->nh_dev; 148 goto ok; 149 } 150 } 151 return; 152 #else 153 found = FIB_RES_DEV(res); 154 if (found != oif) 155 return; 156 #endif 157 ok: 158 switch (priv->result) { 159 case NFT_FIB_RESULT_OIF: 160 *dest = found->ifindex; 161 break; 162 case NFT_FIB_RESULT_OIFNAME: 163 strncpy((char *)dest, found->name, IFNAMSIZ); 164 break; 165 default: 166 WARN_ON_ONCE(1); 167 break; 168 } 169 } 170 EXPORT_SYMBOL_GPL(nft_fib4_eval); 171 172 static struct nft_expr_type nft_fib4_type; 173 174 static const struct nft_expr_ops nft_fib4_type_ops = { 175 .type = &nft_fib4_type, 176 .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), 177 .eval = nft_fib4_eval_type, 178 .init = nft_fib_init, 179 .dump = nft_fib_dump, 180 .validate = nft_fib_validate, 181 }; 182 183 static const struct nft_expr_ops nft_fib4_ops = { 184 .type = &nft_fib4_type, 185 .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), 186 .eval = nft_fib4_eval, 187 .init = nft_fib_init, 188 .dump = nft_fib_dump, 189 .validate = nft_fib_validate, 190 }; 191 192 static const struct nft_expr_ops * 193 nft_fib4_select_ops(const struct nft_ctx *ctx, 194 const struct nlattr * const tb[]) 195 { 196 enum nft_fib_result result; 197 198 if (!tb[NFTA_FIB_RESULT]) 199 return ERR_PTR(-EINVAL); 200 201 result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); 202 203 switch (result) { 204 case NFT_FIB_RESULT_OIF: 205 return &nft_fib4_ops; 206 case NFT_FIB_RESULT_OIFNAME: 207 return &nft_fib4_ops; 208 case NFT_FIB_RESULT_ADDRTYPE: 209 return &nft_fib4_type_ops; 210 default: 211 return ERR_PTR(-EOPNOTSUPP); 212 } 213 } 214 215 static struct nft_expr_type nft_fib4_type __read_mostly = { 216 .name = "fib", 217 .select_ops = &nft_fib4_select_ops, 218 .policy = nft_fib_policy, 219 .maxattr = NFTA_FIB_MAX, 220 .family = NFPROTO_IPV4, 221 .owner = THIS_MODULE, 222 }; 223 224 static int __init nft_fib4_module_init(void) 225 { 226 return nft_register_expr(&nft_fib4_type); 227 } 228 229 static void __exit nft_fib4_module_exit(void) 230 { 231 nft_unregister_expr(&nft_fib4_type); 232 } 233 234 module_init(nft_fib4_module_init); 235 module_exit(nft_fib4_module_exit); 236 MODULE_LICENSE("GPL"); 237 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 238 MODULE_ALIAS_NFT_AF_EXPR(2, "fib"); 239