net/xfrm/xfrm_output.c

Source file repositories/reference/linux-study-clean/net/xfrm/xfrm_output.c

File Facts

System
Linux kernel
Corpus path
net/xfrm/xfrm_output.c
Extension
.c
Size
23481 bytes
Lines
974
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: exported/initcall integration point
Status
integration 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

switch (**nexthdr) {
		case NEXTHDR_HOP:
			break;
		case NEXTHDR_ROUTING:
			if (type == IPPROTO_ROUTING && offset + 3 <= packet_len) {
				struct ipv6_rt_hdr *rt;

				rt = (struct ipv6_rt_hdr *)(nh + offset);
				if (rt->type != 0)
					return offset;
			}
			found_rhdr = 1;
			break;
		case NEXTHDR_DEST:
			/* HAO MUST NOT appear more than once.
			 * XXX: It is better to try to find by the end of
			 * XXX: packet if HAO exists.
			 */
			if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) {
				net_dbg_ratelimited("mip6: hao exists already, override\n");
				return offset;
			}

			if (found_rhdr)
				return offset;

			break;
		default:
			return offset;
		}

		if (offset + sizeof(struct ipv6_opt_hdr) > packet_len)
			return -EINVAL;

		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
						 offset);
		offset += ipv6_optlen(exthdr);
		if (offset > IPV6_MAXPLEN)
			return -EINVAL;
		*nexthdr = &exthdr->nexthdr;
	}

	return -EINVAL;
}
#endif

#if IS_ENABLED(CONFIG_IPV6)
static int xfrm6_hdr_offset(struct xfrm_state *x, struct sk_buff *skb, u8 **prevhdr)
{
	switch (x->type->proto) {
#if IS_ENABLED(CONFIG_IPV6_MIP6)
	case IPPROTO_DSTOPTS:
	case IPPROTO_ROUTING:
		return mip6_rthdr_offset(skb, prevhdr, x->type->proto);
#endif
	default:
		break;
	}

	return ip6_find_1stfragopt(skb, prevhdr);
}
#endif

/* Add encapsulation header.
 *
 * The IP header and mutable extension headers will be moved forward to make
 * space for the encapsulation header.
 */
static int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb)
{
#if IS_ENABLED(CONFIG_IPV6)
	struct ipv6hdr *iph;
	u8 *prevhdr;
	int hdr_len;

	iph = ipv6_hdr(skb);
	if (!skb->inner_protocol)
		skb_set_inner_transport_header(skb,
					       skb_transport_offset(skb));

	hdr_len = xfrm6_hdr_offset(x, skb, &prevhdr);
	if (hdr_len < 0)
		return hdr_len;
	skb_set_mac_header(skb,
			   (prevhdr - x->props.header_len) - skb->data);
	skb_set_network_header(skb, -x->props.header_len);
	skb->transport_header = skb->network_header + hdr_len;
	__skb_pull(skb, hdr_len);
	memmove(ipv6_hdr(skb), iph, hdr_len);
	return 0;

Annotation

Implementation Notes