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 *dest = 0; 126 127 if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) 128 return; 129 130 switch (res.type) { 131 case RTN_UNICAST: 132 break; 133 case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */ 134 return; 135 default: 136 break; 137 } 138 139 if (!oif) { 140 found = FIB_RES_DEV(res); 141 goto ok; 142 } 143 144 #ifdef CONFIG_IP_ROUTE_MULTIPATH 145 for (i = 0; i < res.fi->fib_nhs; i++) { 146 struct fib_nh *nh = &res.fi->fib_nh[i]; 147 148 if (nh->nh_dev == oif) { 149 found = nh->nh_dev; 150 goto ok; 151 } 152 } 153 return; 154 #else 155 found = FIB_RES_DEV(res); 156 if (found != oif) 157 return; 158 #endif 159 ok: 160 switch (priv->result) { 161 case NFT_FIB_RESULT_OIF: 162 *dest = found->ifindex; 163 break; 164 case NFT_FIB_RESULT_OIFNAME: 165 strncpy((char *)dest, found->name, IFNAMSIZ); 166 break; 167 default: 168 WARN_ON_ONCE(1); 169 break; 170 } 171 } 172 EXPORT_SYMBOL_GPL(nft_fib4_eval); 173 174 static struct nft_expr_type nft_fib4_type; 175 176 static const struct nft_expr_ops nft_fib4_type_ops = { 177 .type = &nft_fib4_type, 178 .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), 179 .eval = nft_fib4_eval_type, 180 .init = nft_fib_init, 181 .dump = nft_fib_dump, 182 .validate = nft_fib_validate, 183 }; 184 185 static const struct nft_expr_ops nft_fib4_ops = { 186 .type = &nft_fib4_type, 187 .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), 188 .eval = nft_fib4_eval, 189 .init = nft_fib_init, 190 .dump = nft_fib_dump, 191 .validate = nft_fib_validate, 192 }; 193 194 static const struct nft_expr_ops * 195 nft_fib4_select_ops(const struct nft_ctx *ctx, 196 const struct nlattr * const tb[]) 197 { 198 enum nft_fib_result result; 199 200 if (!tb[NFTA_FIB_RESULT]) 201 return ERR_PTR(-EINVAL); 202 203 result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); 204 205 switch (result) { 206 case NFT_FIB_RESULT_OIF: 207 return &nft_fib4_ops; 208 case NFT_FIB_RESULT_OIFNAME: 209 return &nft_fib4_ops; 210 case NFT_FIB_RESULT_ADDRTYPE: 211 return &nft_fib4_type_ops; 212 default: 213 return ERR_PTR(-EOPNOTSUPP); 214 } 215 } 216 217 static struct nft_expr_type nft_fib4_type __read_mostly = { 218 .name = "fib", 219 .select_ops = &nft_fib4_select_ops, 220 .policy = nft_fib_policy, 221 .maxattr = NFTA_FIB_MAX, 222 .family = NFPROTO_IPV4, 223 .owner = THIS_MODULE, 224 }; 225 226 static int __init nft_fib4_module_init(void) 227 { 228 return nft_register_expr(&nft_fib4_type); 229 } 230 231 static void __exit nft_fib4_module_exit(void) 232 { 233 nft_unregister_expr(&nft_fib4_type); 234 } 235 236 module_init(nft_fib4_module_init); 237 module_exit(nft_fib4_module_exit); 238 MODULE_LICENSE("GPL"); 239 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 240 MODULE_ALIAS_NFT_AF_EXPR(2, "fib"); 241