File Index
Files and directories
Sign in
FILE

nf_socket_ipv4.c

File preview with syntax highlighting.

linux-6.19.14/net/ipv4/netfilter/nf_socket_ipv4.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2007-2008 BalaBit IT Ltd.
  4 * Author: Krisztian Kovacs
  5 */
  6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  7#include <linux/module.h>
  8#include <linux/skbuff.h>
  9#include <net/tcp.h>
 10#include <net/udp.h>
 11#include <net/icmp.h>
 12#include <net/sock.h>
 13#include <net/inet_sock.h>
 14#include <net/netfilter/nf_socket.h>
 15#if IS_ENABLED(CONFIG_NF_CONNTRACK)
 16#include <net/netfilter/nf_conntrack.h>
 17#endif
 18
 19static int
 20extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol,
 21		     __be32 *raddr, __be32 *laddr,
 22		     __be16 *rport, __be16 *lport)
 23{
 24	unsigned int outside_hdrlen = ip_hdrlen(skb);
 25	struct iphdr *inside_iph, _inside_iph;
 26	struct icmphdr *icmph, _icmph;
 27	__be16 *ports, _ports[2];
 28
 29	icmph = skb_header_pointer(skb, outside_hdrlen,
 30				   sizeof(_icmph), &_icmph);
 31	if (icmph == NULL)
 32		return 1;
 33
 34	if (!icmp_is_err(icmph->type))
 35		return 1;
 36
 37	inside_iph = skb_header_pointer(skb, outside_hdrlen +
 38					sizeof(struct icmphdr),
 39					sizeof(_inside_iph), &_inside_iph);
 40	if (inside_iph == NULL)
 41		return 1;
 42
 43	if (inside_iph->protocol != IPPROTO_TCP &&
 44	    inside_iph->protocol != IPPROTO_UDP)
 45		return 1;
 46
 47	ports = skb_header_pointer(skb, outside_hdrlen +
 48				   sizeof(struct icmphdr) +
 49				   (inside_iph->ihl << 2),
 50				   sizeof(_ports), &_ports);
 51	if (ports == NULL)
 52		return 1;
 53
 54	/* the inside IP packet is the one quoted from our side, thus
 55	 * its saddr is the local address */
 56	*protocol = inside_iph->protocol;
 57	*laddr = inside_iph->saddr;
 58	*lport = ports[0];
 59	*raddr = inside_iph->daddr;
 60	*rport = ports[1];
 61
 62	return 0;
 63}
 64
 65static struct sock *
 66nf_socket_get_sock_v4(struct net *net, struct sk_buff *skb, const int doff,
 67		      const u8 protocol,
 68		      const __be32 saddr, const __be32 daddr,
 69		      const __be16 sport, const __be16 dport,
 70		      const struct net_device *in)
 71{
 72	switch (protocol) {
 73	case IPPROTO_TCP:
 74		return inet_lookup(net, skb, doff, saddr, sport, daddr, dport,
 75				   in->ifindex);
 76	case IPPROTO_UDP:
 77		return udp4_lib_lookup(net, saddr, sport, daddr, dport,
 78				       in->ifindex);
 79	}
 80	return NULL;
 81}
 82
 83struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb,
 84				  const struct net_device *indev)
 85{
 86	__be32 daddr, saddr;
 87	__be16 dport, sport;
 88	const struct iphdr *iph = ip_hdr(skb);
 89	struct sk_buff *data_skb = NULL;
 90	u8 protocol;
 91#if IS_ENABLED(CONFIG_NF_CONNTRACK)
 92	enum ip_conntrack_info ctinfo;
 93	struct nf_conn const *ct;
 94#endif
 95	int doff = 0;
 96
 97	if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
 98		struct tcphdr _hdr;
 99		struct udphdr *hp;
100
101		hp = skb_header_pointer(skb, ip_hdrlen(skb),
102					iph->protocol == IPPROTO_UDP ?
103					sizeof(*hp) : sizeof(_hdr), &_hdr);
104		if (hp == NULL)
105			return NULL;
106
107		protocol = iph->protocol;
108		saddr = iph->saddr;
109		sport = hp->source;
110		daddr = iph->daddr;
111		dport = hp->dest;
112		data_skb = (struct sk_buff *)skb;
113		doff = iph->protocol == IPPROTO_TCP ?
114			ip_hdrlen(skb) + __tcp_hdrlen((struct tcphdr *)hp) :
115			ip_hdrlen(skb) + sizeof(*hp);
116
117	} else if (iph->protocol == IPPROTO_ICMP) {
118		if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
119					 &sport, &dport))
120			return NULL;
121	} else {
122		return NULL;
123	}
124
125#if IS_ENABLED(CONFIG_NF_CONNTRACK)
126	/* Do the lookup with the original socket address in
127	 * case this is a reply packet of an established
128	 * SNAT-ted connection.
129	 */
130	ct = nf_ct_get(skb, &ctinfo);
131	if (ct &&
132	    ((iph->protocol != IPPROTO_ICMP &&
133	      ctinfo == IP_CT_ESTABLISHED_REPLY) ||
134	     (iph->protocol == IPPROTO_ICMP &&
135	      ctinfo == IP_CT_RELATED_REPLY)) &&
136	    (ct->status & IPS_SRC_NAT_DONE)) {
137
138		daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
139		dport = (iph->protocol == IPPROTO_TCP) ?
140			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port :
141			ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
142	}
143#endif
144
145	return nf_socket_get_sock_v4(net, data_skb, doff, protocol, saddr,
146				     daddr, sport, dport, indev);
147}
148EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v4);
149
150MODULE_LICENSE("GPL");
151MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
152MODULE_DESCRIPTION("Netfilter IPv4 socket lookup infrastructure");
~/ Read-only file preview.