net/ipv6/ila/ila_lwt.c

Source file repositories/reference/linux-study-clean/net/ipv6/ila/ila_lwt.c

File Facts

System
Linux kernel
Corpus path
net/ipv6/ila/ila_lwt.c
Extension
.c
Size
7996 bytes
Lines
333
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: implementation source
Status
source implementation candidate

Why This File Exists

Networking stack implementation surface: socket APIs, protocol dispatch, packet flow, routing, filtering, and network namespaces.

Dependency Surface

Detected Declarations

Annotated Snippet

struct ila_lwt {
	struct ila_params p;
	struct dst_cache dst_cache;
	u32 connected : 1;
	u32 lwt_output : 1;
};

static inline struct ila_lwt *ila_lwt_lwtunnel(
	struct lwtunnel_state *lwt)
{
	return (struct ila_lwt *)lwt->data;
}

static inline struct ila_params *ila_params_lwtunnel(
	struct lwtunnel_state *lwt)
{
	return &ila_lwt_lwtunnel(lwt)->p;
}

static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct dst_entry *orig_dst = skb_dst(skb);
	struct rt6_info *rt = dst_rt6_info(orig_dst);
	struct ila_lwt *ilwt = ila_lwt_lwtunnel(orig_dst->lwtstate);
	struct dst_entry *dst;
	int err = -EINVAL;

	if (skb->protocol != htons(ETH_P_IPV6))
		goto drop;

	if (ilwt->lwt_output)
		ila_update_ipv6_locator(skb,
					ila_params_lwtunnel(orig_dst->lwtstate),
					true);

	if (rt->rt6i_flags & (RTF_GATEWAY | RTF_CACHE)) {
		/* Already have a next hop address in route, no need for
		 * dest cache route.
		 */
		return orig_dst->lwtstate->orig_output(net, sk, skb);
	}

	local_bh_disable();
	dst = dst_cache_get(&ilwt->dst_cache);
	local_bh_enable();
	if (unlikely(!dst)) {
		struct ipv6hdr *ip6h = ipv6_hdr(skb);
		struct flowi6 fl6;

		/* Lookup a route for the new destination. Take into
		 * account that the base route may already have a gateway.
		 */

		memset(&fl6, 0, sizeof(fl6));
		fl6.flowi6_oif = dst_dev(orig_dst)->ifindex;
		fl6.flowi6_iif = LOOPBACK_IFINDEX;
		fl6.daddr = *rt6_nexthop(dst_rt6_info(orig_dst),
					 &ip6h->daddr);

		dst = ip6_route_output(net, NULL, &fl6);
		if (dst->error) {
			err = -EHOSTUNREACH;
			dst_release(dst);
			goto drop;
		}

		dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
		if (IS_ERR(dst)) {
			err = PTR_ERR(dst);
			goto drop;
		}

		/* cache only if we don't create a dst reference loop */
		if (ilwt->connected && orig_dst->lwtstate != dst->lwtstate) {
			local_bh_disable();
			dst_cache_set_ip6(&ilwt->dst_cache, dst, &fl6.saddr);
			local_bh_enable();
		}
	}

	skb_dst_drop(skb);
	skb_dst_set(skb, dst);
	return dst_output(net, sk, skb);

drop:
	kfree_skb(skb);
	return err;
}

static int ila_input(struct sk_buff *skb)

Annotation

Implementation Notes