/* called under rcu_read_lock */ static int vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4) { struct fib_result res = { .tclassid = 0 }; struct net *net = dev_net(dev); u32 orig_tos = fl4->flowi4_tos; u8 flags = fl4->flowi4_flags; u8 scope = fl4->flowi4_scope; u8 tos = RT_FL_TOS(fl4); int rc; if (unlikely(!fl4->daddr)) return 0; fl4->flowi4_flags |= FLOWI_FLAG_SKIP_NH_OIF; fl4->flowi4_iif = LOOPBACK_IFINDEX; /* make sure oif is set to VRF device for lookup */ fl4->flowi4_oif = dev->ifindex; fl4->flowi4_tos = tos & IPTOS_RT_MASK; fl4->flowi4_scope = ((tos & RTO_ONLINK) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); rc = fib_lookup(net, fl4, &res, 0); if (!rc) { if (res.type == RTN_LOCAL) fl4->saddr = res.fi->fib_prefsrc ? : fl4->daddr; else fib_select_path(net, &res, fl4, -1); } fl4->flowi4_flags = flags; fl4->flowi4_tos = orig_tos; fl4->flowi4_scope = scope; return rc; }
static bool rpfilter_lookup_reverse(struct net *net, struct flowi4 *fl4, const struct net_device *dev, u8 flags) { struct fib_result res; bool dev_match; int ret __maybe_unused; if (fib_lookup(net, fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) return false; if (res.type != RTN_UNICAST) { if (res.type != RTN_LOCAL || !(flags & XT_RPFILTER_ACCEPT_LOCAL)) return false; } dev_match = false; #ifdef CONFIG_IP_ROUTE_MULTIPATH for (ret = 0; ret < res.fi->fib_nhs; ret++) { struct fib_nh *nh = &res.fi->fib_nh[ret]; if (nh->nh_dev == dev) { dev_match = true; break; } } #else if (FIB_RES_DEV(res) == dev) dev_match = true; #endif return dev_match || flags & XT_RPFILTER_LOOSE; }
static bool rpfilter_lookup_reverse(struct flowi4 *fl4, const struct net_device *dev, u8 flags) { struct fib_result res; bool dev_match; struct net *net = dev_net(dev); int ret __maybe_unused; if (fib_lookup(net, fl4, &res)) return false; if (res.type != RTN_UNICAST) { if (res.type != RTN_LOCAL || !(flags & XT_RPFILTER_ACCEPT_LOCAL)) return false; } dev_match = false; //#ifdef CONFIG_IP_ROUTE_MULTIPATH #if (defined CONFIG_IP_ROUTE_MULTIPATH) || (defined CONFIG_IP_ROUTE_DEFLECTION) for (ret = 0; ret < res.fi->fib_nhs; ret++) { struct fib_nh *nh = &res.fi->fib_nh[ret]; if (nh->nh_dev == dev) { dev_match = true; break; } } #else if (FIB_RES_DEV(res) == dev) dev_match = true; #endif if (dev_match || flags & XT_RPFILTER_LOOSE) return FIB_RES_NH(res).nh_scope <= RT_SCOPE_HOST; return dev_match; }
struct rtable * ip_rt_slow_route (__u32 daddr, int local, struct device *dev) { unsigned hash = ip_rt_hash_code(daddr)^local; struct rtable * rth; struct fib_node * f; struct fib_info * fi; __u32 saddr; #if RT_CACHE_DEBUG >= 2 printk("rt_cache miss @%08x\n", daddr); #endif rth = kmalloc(sizeof(struct rtable), GFP_ATOMIC); if (!rth) { ip_rt_unlock(); return NULL; } if (local) f = fib_lookup_local(daddr, dev); else f = fib_lookup (daddr, dev); if (f) { fi = f->fib_info; f->fib_use++; } if (!f || (fi->fib_flags & RTF_REJECT)) { #ifdef CONFIG_KERNELD char wanted_route[20]; #endif #if RT_CACHE_DEBUG >= 2 printk("rt_route failed @%08x\n", daddr); #endif ip_rt_unlock(); kfree_s(rth, sizeof(struct rtable)); #ifdef CONFIG_KERNELD if (MULTICAST(daddr)) return NULL; daddr=ntohl(daddr); sprintf(wanted_route, "%d.%d.%d.%d", (int)(daddr >> 24) & 0xff, (int)(daddr >> 16) & 0xff, (int)(daddr >> 8) & 0xff, (int)daddr & 0xff); kerneld_route(wanted_route); /* Dynamic route request */ #endif return NULL; }
static uint32_t get_next_hop( uint32_t daddr , uint32_t saddr ) { struct fib_result res; struct flowi fl = { .nl_u = { .ip4_u = { .daddr = daddr, .saddr = saddr, .tos = 0, .scope = RT_SCOPE_UNIVERSE, } }, .mark = 0, .iif = multi_route_indev->ifindex }; struct net * net = dev_net(multi_route_indev); struct fib_nh *mrnh; fib_lookup(net, &fl, &res); mrnh = res.fi->fib_nh; return mrnh->nh_gw; }
int ip_do_nat(struct sk_buff *skb) { struct rtable *rt = (struct rtable*)skb->dst; struct iphdr *iph = skb->nh.iph; u32 odaddr = iph->daddr; u32 osaddr = iph->saddr; u16 check; IPCB(skb)->flags |= IPSKB_TRANSLATED; /* Rewrite IP header */ iph->daddr = rt->rt_dst_map; iph->saddr = rt->rt_src_map; iph->check = 0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); /* If it is the first fragment, rewrite protocol headers */ if (!(iph->frag_off & htons(IP_OFFSET))) { u16 *cksum; switch(iph->protocol) { case IPPROTO_TCP: cksum = (u16*)&((struct tcphdr*)(((char*)iph) + (iph->ihl<<2)))->check; if ((u8*)(cksum+1) > skb->tail) goto truncated; check = *cksum; if (skb->ip_summed != CHECKSUM_HW) check = ~check; check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, check); check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check); if (skb->ip_summed == CHECKSUM_HW) check = ~check; *cksum = check; break; case IPPROTO_UDP: cksum = (u16*)&((struct udphdr*)(((char*)iph) + (iph->ihl<<2)))->check; if ((u8*)(cksum+1) > skb->tail) goto truncated; if ((check = *cksum) != 0) { check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check); check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check); *cksum = check ? : 0xFFFF; } break; case IPPROTO_ICMP: { struct icmphdr *icmph = (struct icmphdr*)((char*)iph + (iph->ihl<<2)); struct iphdr *ciph; u32 idaddr, isaddr; int updated; if ((icmph->type != ICMP_DEST_UNREACH) && (icmph->type != ICMP_TIME_EXCEEDED) && (icmph->type != ICMP_PARAMETERPROB)) break; ciph = (struct iphdr *) (icmph + 1); if ((u8*)(ciph+1) > skb->tail) goto truncated; isaddr = ciph->saddr; idaddr = ciph->daddr; updated = 0; if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr) { ciph->saddr = iph->daddr; updated = 1; } if (rt->rt_flags&RTCF_SNAT) { if (ciph->daddr != osaddr) { struct fib_result res; struct rt_key key; unsigned flags = 0; key.src = ciph->daddr; key.dst = ciph->saddr; key.iif = skb->dev->ifindex; key.oif = 0; #ifdef CONFIG_IP_ROUTE_TOS key.tos = RT_TOS(ciph->tos); #endif #ifdef CONFIG_IP_ROUTE_FWMARK key.fwmark = 0; #endif /* Use fib_lookup() until we get our own * hash table of NATed hosts -- Rani */ if (fib_lookup(&key, &res) == 0) { if (res.r) { ciph->daddr = fib_rules_policy(ciph->daddr, &res, &flags); if (ciph->daddr != idaddr) updated = 1; } fib_res_put(&res); } } else { ciph->daddr = iph->saddr; updated = 1; } } if (updated) { cksum = &icmph->checksum; /* Using tcpudp primitive. Why not? */ check = csum_tcpudp_magic(ciph->saddr, ciph->daddr, 0, 0, ~(*cksum)); *cksum = csum_tcpudp_magic(~isaddr, ~idaddr, 0, 0, ~check); } break; } default: break; }
void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_fib *priv = nft_expr_priv(expr); int noff = skb_network_offset(pkt->skb); u32 *dest = ®s->data[priv->dreg]; struct iphdr *iph, _iph; struct fib_result res; struct flowi4 fl4 = { .flowi4_scope = RT_SCOPE_UNIVERSE, .flowi4_iif = LOOPBACK_IFINDEX, }; const struct net_device *oif; struct net_device *found; #ifdef CONFIG_IP_ROUTE_MULTIPATH int i; #endif /* * Do not set flowi4_oif, it restricts results (for example, asking * for oif 3 will get RTN_UNICAST result even if the daddr exits * on another interface. * * Search results for the desired outinterface instead. */ if (priv->flags & NFTA_FIB_F_OIF) oif = nft_out(pkt); else if (priv->flags & NFTA_FIB_F_IIF) oif = nft_in(pkt); else oif = NULL; if (nft_hook(pkt) == NF_INET_PRE_ROUTING && nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { nft_fib_store_result(dest, priv, pkt, nft_in(pkt)->ifindex); return; } iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph); if (!iph) { regs->verdict.code = NFT_BREAK; return; } if (ipv4_is_zeronet(iph->saddr)) { if (ipv4_is_lbcast(iph->daddr) || ipv4_is_local_multicast(iph->daddr)) { nft_fib_store_result(dest, priv, pkt, get_ifindex(pkt->skb->dev)); return; } } if (priv->flags & NFTA_FIB_F_MARK) fl4.flowi4_mark = pkt->skb->mark; fl4.flowi4_tos = iph->tos & DSCP_BITS; if (priv->flags & NFTA_FIB_F_DADDR) { fl4.daddr = iph->daddr; fl4.saddr = get_saddr(iph->saddr); } else { fl4.daddr = iph->saddr; fl4.saddr = get_saddr(iph->daddr); } *dest = 0; if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) return; switch (res.type) { case RTN_UNICAST: break; case RTN_LOCAL: /* Should not see RTN_LOCAL here */ return; default: break; } if (!oif) { found = FIB_RES_DEV(res); goto ok; } #ifdef CONFIG_IP_ROUTE_MULTIPATH for (i = 0; i < res.fi->fib_nhs; i++) { struct fib_nh *nh = &res.fi->fib_nh[i]; if (nh->nh_dev == oif) { found = nh->nh_dev; goto ok; } } return; #else found = FIB_RES_DEV(res); if (found != oif) return; #endif ok: switch (priv->result) { case NFT_FIB_RESULT_OIF: *dest = found->ifindex; break; case NFT_FIB_RESULT_OIFNAME: strncpy((char *)dest, found->name, IFNAMSIZ); break; default: WARN_ON_ONCE(1); break; } }