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