Example #1
0
/* PF_BRIDGE/PRE_ROUTING: Undo the changes made for ip6tables
 * PREROUTING and continue the bridge PRE_ROUTING hook. See comment
 * for br_nf_pre_routing_finish(), same logic is used here but
 * equivalent IPv6 function ip6_route_input() called indirectly.
 */
static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
{
	struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
	struct rtable *rt;
	struct net_device *dev = skb->dev;
	const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();

	nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;

	if (nf_bridge->pkt_otherhost) {
		skb->pkt_type = PACKET_OTHERHOST;
		nf_bridge->pkt_otherhost = false;
	}
	nf_bridge->in_prerouting = 0;
	if (br_nf_ipv6_daddr_was_changed(skb, nf_bridge)) {
		skb_dst_drop(skb);
		v6ops->route_input(skb);

		if (skb_dst(skb)->error) {
			kfree_skb(skb);
			return 0;
		}

		if (skb_dst(skb)->dev == dev) {
			skb->dev = nf_bridge->physindev;
			nf_bridge_update_protocol(skb);
			nf_bridge_push_encap_header(skb);
			NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
				       sk, skb, skb->dev, NULL,
				       br_nf_pre_routing_finish_bridge,
				       1);
			return 0;
		}
		ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
		skb->pkt_type = PACKET_HOST;
	} else {
		rt = bridge_parent_rtable(nf_bridge->physindev);
		if (!rt) {
			kfree_skb(skb);
			return 0;
		}
		skb_dst_set_noref(skb, &rt->dst);
	}

	skb->dev = nf_bridge->physindev;
	nf_bridge_update_protocol(skb);
	nf_bridge_push_encap_header(skb);
	NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
		       skb->dev, NULL,
		       br_handle_frame_finish, 1);

	return 0;
}
static u32 match_lookup_rt6(struct net *net, const struct net_device *dev,
			    const struct in6_addr *addr, u16 mask)
{
	const struct nf_afinfo *afinfo;
	struct flowi6 flow;
	struct rt6_info *rt;
	u32 ret = 0;
	int route_err;

	memset(&flow, 0, sizeof(flow));
	flow.daddr = *addr;
	if (dev)
		flow.flowi6_oif = dev->ifindex;

	rcu_read_lock();

	afinfo = nf_get_afinfo(NFPROTO_IPV6);
	if (afinfo != NULL) {
		const struct nf_ipv6_ops *v6ops;

		if (dev && (mask & XT_ADDRTYPE_LOCAL)) {
			v6ops = nf_get_ipv6_ops();
			if (v6ops && v6ops->chk_addr(net, addr, dev, true))
				ret = XT_ADDRTYPE_LOCAL;
		}
		route_err = afinfo->route(net, (struct dst_entry **)&rt,
					  flowi6_to_flowi(&flow), false);
	} else {
		route_err = 1;
	}
	rcu_read_unlock();

	if (route_err)
		return XT_ADDRTYPE_UNREACHABLE;

	if (rt->rt6i_flags & RTF_REJECT)
		ret = XT_ADDRTYPE_UNREACHABLE;

	if (dev == NULL && rt->rt6i_flags & RTF_LOCAL)
		ret |= XT_ADDRTYPE_LOCAL;
	if (rt->rt6i_flags & RTF_ANYCAST)
		ret |= XT_ADDRTYPE_ANYCAST;

	dst_release(&rt->dst);
	return ret;
}
Example #3
0
static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct nf_bridge_info *nf_bridge;
	unsigned int mtu_reserved;

	mtu_reserved = nf_bridge_mtu_reduction(skb);

	if (skb_is_gso(skb) || skb->len + mtu_reserved <= skb->dev->mtu) {
		nf_bridge_info_free(skb);
		return br_dev_queue_push_xmit(net, sk, skb);
	}

	nf_bridge = nf_bridge_info_get(skb);

	/* This is wrong! We should preserve the original fragment
	 * boundaries by preserving frag_list rather than refragmenting.
	 */
	if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) &&
	    skb->protocol == htons(ETH_P_IP)) {
		struct brnf_frag_data *data;

		if (br_validate_ipv4(net, skb))
			goto drop;

		IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;

		nf_bridge_update_protocol(skb);

		data = this_cpu_ptr(&brnf_frag_data_storage);

		data->vlan_tci = skb->vlan_tci;
		data->vlan_proto = skb->vlan_proto;
		data->encap_size = nf_bridge_encap_header_len(skb);
		data->size = ETH_HLEN + data->encap_size;

		skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
						 data->size);

		return br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit);
	}
	if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) &&
	    skb->protocol == htons(ETH_P_IPV6)) {
		const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
		struct brnf_frag_data *data;

		if (br_validate_ipv6(net, skb))
			goto drop;

		IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;

		nf_bridge_update_protocol(skb);

		data = this_cpu_ptr(&brnf_frag_data_storage);
		data->encap_size = nf_bridge_encap_header_len(skb);
		data->size = ETH_HLEN + data->encap_size;

		skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
						 data->size);

		if (v6ops)
			return v6ops->fragment(net, sk, skb, br_nf_push_frag_xmit);

		kfree_skb(skb);
		return -EMSGSIZE;
	}
	nf_bridge_info_free(skb);
	return br_dev_queue_push_xmit(net, sk, skb);
 drop:
	kfree_skb(skb);
	return 0;
}
Example #4
0
static u32 __nft_fib6_eval_type(const struct nft_fib *priv,
				const struct nft_pktinfo *pkt)
{
	const struct net_device *dev = NULL;
	const struct nf_ipv6_ops *v6ops;
	const struct nf_afinfo *afinfo;
	int route_err, addrtype;
	struct rt6_info *rt;
	struct flowi6 fl6 = {
		.flowi6_iif = LOOPBACK_IFINDEX,
		.flowi6_proto = pkt->tprot,
	};
	u32 ret = 0;

	afinfo = nf_get_afinfo(NFPROTO_IPV6);
	if (!afinfo)
		return RTN_UNREACHABLE;

	if (priv->flags & NFTA_FIB_F_IIF)
		dev = nft_in(pkt);
	else if (priv->flags & NFTA_FIB_F_OIF)
		dev = nft_out(pkt);

	nft_fib6_flowi_init(&fl6, priv, pkt, dev);

	v6ops = nf_get_ipv6_ops();
	if (dev && v6ops && v6ops->chk_addr(nft_net(pkt), &fl6.daddr, dev, true))
		ret = RTN_LOCAL;

	route_err = afinfo->route(nft_net(pkt), (struct dst_entry **)&rt,
				  flowi6_to_flowi(&fl6), false);
	if (route_err)
		goto err;

	if (rt->rt6i_flags & RTF_REJECT) {
		route_err = rt->dst.error;
		dst_release(&rt->dst);
		goto err;
	}

	if (ipv6_anycast_destination((struct dst_entry *)rt, &fl6.daddr))
		ret = RTN_ANYCAST;
	else if (!dev && rt->rt6i_flags & RTF_LOCAL)
		ret = RTN_LOCAL;

	dst_release(&rt->dst);

	if (ret)
		return ret;

	addrtype = ipv6_addr_type(&fl6.daddr);

	if (addrtype & IPV6_ADDR_MULTICAST)
		return RTN_MULTICAST;
	if (addrtype & IPV6_ADDR_UNICAST)
		return RTN_UNICAST;

	return RTN_UNSPEC;
 err:
	switch (route_err) {
	case -EINVAL:
		return RTN_BLACKHOLE;
	case -EACCES:
		return RTN_PROHIBIT;
	case -EAGAIN:
		return RTN_THROW;
	default:
		break;
	}

	return RTN_UNREACHABLE;
}

void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
			const struct nft_pktinfo *pkt)
{
	const struct nft_fib *priv = nft_expr_priv(expr);
	u32 *dest = &regs->data[priv->dreg];

	*dest = __nft_fib6_eval_type(priv, pkt);
}
EXPORT_SYMBOL_GPL(nft_fib6_eval_type);

void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
		   const struct nft_pktinfo *pkt)
{
	const struct nft_fib *priv = nft_expr_priv(expr);
	const struct net_device *oif = NULL;
	u32 *dest = &regs->data[priv->dreg];
	struct flowi6 fl6 = {
		.flowi6_iif = LOOPBACK_IFINDEX,
		.flowi6_proto = pkt->tprot,
	};
	struct rt6_info *rt;
	int lookup_flags;

	if (priv->flags & NFTA_FIB_F_IIF)
		oif = nft_in(pkt);
	else if (priv->flags & NFTA_FIB_F_OIF)
		oif = nft_out(pkt);

	lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif);

	if (nft_hook(pkt) == NF_INET_PRE_ROUTING &&
	    nft_fib_is_loopback(pkt->skb, nft_in(pkt))) {
		nft_fib_store_result(dest, priv->result, pkt,
				     nft_in(pkt)->ifindex);
		return;
	}

	*dest = 0;
 again:
	rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, lookup_flags);
	if (rt->dst.error)
		goto put_rt_err;

	/* Should not see RTF_LOCAL here */
	if (rt->rt6i_flags & (RTF_REJECT | RTF_ANYCAST | RTF_LOCAL))
		goto put_rt_err;

	if (oif && oif != rt->rt6i_idev->dev) {
		/* multipath route? Try again with F_IFACE */
		if ((lookup_flags & RT6_LOOKUP_F_IFACE) == 0) {
			lookup_flags |= RT6_LOOKUP_F_IFACE;
			fl6.flowi6_oif = oif->ifindex;
			ip6_rt_put(rt);
			goto again;
		}
	}

	switch (priv->result) {
	case NFT_FIB_RESULT_OIF:
		*dest = rt->rt6i_idev->dev->ifindex;
		break;
	case NFT_FIB_RESULT_OIFNAME:
		strncpy((char *)dest, rt->rt6i_idev->dev->name, IFNAMSIZ);
		break;
	default:
		WARN_ON_ONCE(1);
		break;
	}

 put_rt_err:
	ip6_rt_put(rt);
}
EXPORT_SYMBOL_GPL(nft_fib6_eval);

static struct nft_expr_type nft_fib6_type;

static const struct nft_expr_ops nft_fib6_type_ops = {
	.type		= &nft_fib6_type,
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_fib)),
	.eval		= nft_fib6_eval_type,
	.init		= nft_fib_init,
	.dump		= nft_fib_dump,
	.validate	= nft_fib_validate,
};

static const struct nft_expr_ops nft_fib6_ops = {
	.type		= &nft_fib6_type,
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_fib)),
	.eval		= nft_fib6_eval,
	.init		= nft_fib_init,
	.dump		= nft_fib_dump,
	.validate	= nft_fib_validate,
};

static const struct nft_expr_ops *
nft_fib6_select_ops(const struct nft_ctx *ctx,
		    const struct nlattr * const tb[])
{
	enum nft_fib_result result;

	if (!tb[NFTA_FIB_RESULT])
		return ERR_PTR(-EINVAL);

	result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));

	switch (result) {
	case NFT_FIB_RESULT_OIF:
		return &nft_fib6_ops;
	case NFT_FIB_RESULT_OIFNAME:
		return &nft_fib6_ops;
	case NFT_FIB_RESULT_ADDRTYPE:
		return &nft_fib6_type_ops;
	default:
		return ERR_PTR(-EOPNOTSUPP);
	}
}

static struct nft_expr_type nft_fib6_type __read_mostly = {
	.name		= "fib",
	.select_ops	= &nft_fib6_select_ops,
	.policy		= nft_fib_policy,
	.maxattr	= NFTA_FIB_MAX,
	.family		= NFPROTO_IPV6,
	.owner		= THIS_MODULE,
};

static int __init nft_fib6_module_init(void)
{
	return nft_register_expr(&nft_fib6_type);
}

static void __exit nft_fib6_module_exit(void)
{
	nft_unregister_expr(&nft_fib6_type);
}
module_init(nft_fib6_module_init);
module_exit(nft_fib6_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Florian Westphal <*****@*****.**>");
MODULE_ALIAS_NFT_AF_EXPR(10, "fib");