netinet: allow per protocol random IP id control, single out IPSEC

A globally enabled random IP id generation maybe useful in most IP
contexts, but it may be unnecessary in the case of IPsec encapsulated
packets because IPsec can be configured to use anti-replay windows.

This commit adds a new net.inet.ipsec.random_id sysctl to control whether
or not IPsec packets should use random IP id generation.

Rest of the protocols/modules are still controlled by the global
net.inet.ip.random_id, but can be easily augmented with a knob.

Reviewed by:		glebius
Sponsored by:		Stormshield
Differential Revision:	https://reviews.freebsd.org/D49164
This commit is contained in:
acazuc 2025-03-03 14:21:15 +01:00 committed by Gleb Smirnoff
parent 80b64ef0a1
commit 70703aa922
18 changed files with 44 additions and 31 deletions

View file

@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd February 6, 2017
.Dd March 4, 2025
.Dt IPSEC 4
.Os
.Sh NAME
@ -239,6 +239,7 @@ for tweaking the kernel's IPsec behavior:
.It "net.inet.ipsec.debug integer yes"
.It "net.inet.ipsec.natt_cksum_policy integer yes"
.It "net.inet.ipsec.check_policy_history integer yes"
.It "net.inet.ipsec.random_id integer yes"
.It "net.inet6.ipsec6.ecn integer yes"
.It "net.inet6.ipsec6.debug integer yes"
.El
@ -298,6 +299,9 @@ have been decrypted and authenticated.
If this variable is set to a non-zero value, each packet handled by IPsec
is checked against the history of IPsec security associations.
The IPsec security protocol, mode, and SA addresses must match.
.It Li ipsec.random_id
Enables randomization of encapsulated IPv4 packets ID.
By default, ID randomization is not enabled.
.El
.Pp
Variables under the

View file

@ -1256,7 +1256,7 @@ carp_send_ad_locked(struct carp_softc *sc)
ip->ip_ttl = CARP_DFLTTL;
ip->ip_p = IPPROTO_CARP;
ip->ip_sum = 0;
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
ifa = carp_best_ifa(AF_INET, sc->sc_carpdev);
if (ifa != NULL) {
@ -1395,7 +1395,7 @@ vrrp_send_ad_locked(struct carp_softc *sc)
ip->ip_ttl = CARP_DFLTTL;
ip->ip_p = IPPROTO_CARP;
ip->ip_sum = 0;
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
ifa = carp_best_ifa(AF_INET, sc->sc_carpdev);
if (ifa != NULL) {

View file

@ -534,7 +534,7 @@ in_gre_output(struct mbuf *m, int af, int hlen)
#ifdef INET6
case AF_INET6:
gi->gi_ip.ip_tos = 0; /* XXX */
ip_fillid(&gi->gi_ip);
ip_fillid(&gi->gi_ip, V_ip_random_id);
break;
#endif
}

View file

@ -97,9 +97,9 @@
* user wants to, we can turn on random ID generation.
*/
VNET_DEFINE_STATIC(int, ip_rfc6864) = 1;
VNET_DEFINE_STATIC(int, ip_do_randomid) = 0;
#define V_ip_rfc6864 VNET(ip_rfc6864)
#define V_ip_do_randomid VNET(ip_do_randomid)
VNET_DEFINE(int, ip_random_id) = 0;
/*
* Random ID state engine.
@ -126,7 +126,7 @@ VNET_DEFINE_STATIC(struct mtx, ip_id_mtx);
VNET_DEFINE_STATIC(counter_u64_t, ip_id);
#define V_ip_id VNET(ip_id)
static int sysctl_ip_randomid(SYSCTL_HANDLER_ARGS);
static int sysctl_ip_random_id(SYSCTL_HANDLER_ARGS);
static int sysctl_ip_id_change(SYSCTL_HANDLER_ARGS);
static void ip_initid(int);
static uint16_t ip_randomid(void);
@ -136,7 +136,7 @@ static void ipid_sysuninit(void);
SYSCTL_DECL(_net_inet_ip);
SYSCTL_PROC(_net_inet_ip, OID_AUTO, random_id,
CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW | CTLFLAG_MPSAFE,
&VNET_NAME(ip_do_randomid), 0, sysctl_ip_randomid, "IU",
&VNET_NAME(ip_random_id), 0, sysctl_ip_random_id, "IU",
"Assign random ip_id values");
SYSCTL_INT(_net_inet_ip, OID_AUTO, rfc6864, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(ip_rfc6864), 0,
@ -151,22 +151,22 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, random_id_total, CTLFLAG_RD | CTLFLAG_VNET,
&VNET_NAME(random_id_total), 0, "Count of IP IDs created");
static int
sysctl_ip_randomid(SYSCTL_HANDLER_ARGS)
sysctl_ip_random_id(SYSCTL_HANDLER_ARGS)
{
int error, new;
new = V_ip_do_randomid;
new = V_ip_random_id;
error = sysctl_handle_int(oidp, &new, 0, req);
if (error || req->newptr == NULL)
return (error);
if (new != 0 && new != 1)
return (EINVAL);
if (new == V_ip_do_randomid)
if (new == V_ip_random_id)
return (0);
if (new == 1 && V_ip_do_randomid == 0)
if (new == 1 && V_ip_random_id == 0)
ip_initid(8192);
/* We don't free memory when turning random ID off, due to race. */
V_ip_do_randomid = new;
V_ip_random_id = new;
return (0);
}
@ -238,7 +238,7 @@ ip_randomid(void)
}
void
ip_fillid(struct ip *ip)
ip_fillid(struct ip *ip, bool do_randomid)
{
/*
@ -249,7 +249,7 @@ ip_fillid(struct ip *ip)
*/
if (V_ip_rfc6864 && (ip->ip_off & htons(IP_DF)) == htons(IP_DF))
ip->ip_id = 0;
else if (V_ip_do_randomid)
else if (do_randomid)
ip->ip_id = ip_randomid();
else {
counter_u64_add(V_ip_id, 1);

View file

@ -2466,7 +2466,7 @@ pim_register_send_rp(struct ip *ip, struct vif *vifp, struct mbuf *mb_copy,
ip_outer->ip_tos = ip->ip_tos;
if (ip->ip_off & htons(IP_DF))
ip_outer->ip_off |= htons(IP_DF);
ip_fillid(ip_outer);
ip_fillid(ip_outer, V_ip_random_id);
pimhdr = (struct pim_encap_pimhdr *)((caddr_t)ip_outer
+ sizeof(pim_encap_iphdr));
*pimhdr = pim_encap_pimhdr;

View file

@ -368,7 +368,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) {
ip->ip_v = IPVERSION;
ip->ip_hl = hlen >> 2;
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
} else {
/* Header already set, fetch hlen from there */
hlen = ip->ip_hl << 2;

View file

@ -204,6 +204,7 @@ extern int (*legal_vif_num)(int);
extern u_long (*ip_mcast_src)(int);
VNET_DECLARE(int, rsvp_on);
VNET_DECLARE(int, drop_redirect);
VNET_DECLARE(int, ip_random_id);
#define V_ip_id VNET(ip_id)
#define V_ip_defttl VNET(ip_defttl)
@ -216,6 +217,7 @@ VNET_DECLARE(int, drop_redirect);
#define V_ip_mrouter VNET(ip_mrouter)
#define V_rsvp_on VNET(rsvp_on)
#define V_drop_redirect VNET(drop_redirect)
#define V_ip_random_id VNET(ip_random_id)
void inp_freemoptions(struct ip_moptions *);
int inp_getmoptions(struct inpcb *, struct sockopt *);
@ -235,7 +237,7 @@ struct mbuf *
ip_reass(struct mbuf *);
void ip_savecontrol(struct inpcb *, struct mbuf **, struct ip *,
struct mbuf *);
void ip_fillid(struct ip *);
void ip_fillid(struct ip *, bool);
int rip_ctloutput(struct socket *, struct sockopt *);
int ipip_input(struct mbuf **, int *, int);
int rsvp_input(struct mbuf **, int *, int);

View file

@ -601,7 +601,7 @@ rip_send(struct socket *so, int pruflags, struct mbuf *m, struct sockaddr *nam,
* but we got this limitation from the beginning of history.
*/
if (ip->ip_id == 0)
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
/*
* XXX prevent ip_output from overwriting header fields.

View file

@ -4071,7 +4071,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
ip->ip_off = htons(0);
}
/* FreeBSD has a function for ip_id's */
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
ip->ip_ttl = inp->ip_inp.inp.inp_ip_ttl;
ip->ip_len = htons(packet_length);
@ -11197,7 +11197,7 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst,
ip->ip_hl = (sizeof(struct ip) >> 2);
ip->ip_tos = 0;
ip->ip_off = htons(IP_DF);
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
ip->ip_ttl = MODULE_GLOBAL(ip_defttl);
if (port) {
ip->ip_p = IPPROTO_UDP;

View file

@ -119,6 +119,7 @@ VNET_DEFINE(int, ip4_ah_trans_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE;
/* ECN ignore(-1)/forbidden(0)/allowed(1) */
VNET_DEFINE(int, ip4_ipsec_ecn) = 0;
VNET_DEFINE(int, ip4_ipsec_random_id) = 0;
VNET_DEFINE_STATIC(int, ip4_filtertunnel) = 0;
#define V_ip4_filtertunnel VNET(ip4_filtertunnel)
@ -201,6 +202,9 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_MIN_PMTU, min_pmtu,
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_ecn), 0,
"Explicit Congestion Notification handling.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_RANDOM_ID, random_id,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_random_id), 0,
"Assign random ip_id values.");
SYSCTL_INT(_net_inet_ipsec, OID_AUTO, crypto_support,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(crypto_support), 0,
"Crypto driver selection.");

View file

@ -260,6 +260,7 @@ struct ipsecstat {
#define IPSECCTL_DEBUG 12
#define IPSECCTL_ESP_RANDPAD 13
#define IPSECCTL_MIN_PMTU 14
#define IPSECCTL_RANDOM_ID 15
#ifdef _KERNEL
#include <sys/counter.h>
@ -293,6 +294,7 @@ VNET_DECLARE(int, ip4_ah_net_deflev);
VNET_DECLARE(int, ip4_ipsec_dfbit);
VNET_DECLARE(int, ip4_ipsec_min_pmtu);
VNET_DECLARE(int, ip4_ipsec_ecn);
VNET_DECLARE(int, ip4_ipsec_random_id);
VNET_DECLARE(int, crypto_support);
VNET_DECLARE(int, async_crypto);
VNET_DECLARE(int, natt_cksum_policy);
@ -310,6 +312,7 @@ VNET_DECLARE(int, natt_cksum_policy);
#define V_ip4_ipsec_dfbit VNET(ip4_ipsec_dfbit)
#define V_ip4_ipsec_min_pmtu VNET(ip4_ipsec_min_pmtu)
#define V_ip4_ipsec_ecn VNET(ip4_ipsec_ecn)
#define V_ip4_ipsec_random_id VNET(ip4_ipsec_random_id)
#define V_crypto_support VNET(crypto_support)
#define V_async_crypto VNET(async_crypto)
#define V_natt_cksum_policy VNET(natt_cksum_policy)

View file

@ -1200,7 +1200,7 @@ ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
ip->ip_src = saidx->src.sin.sin_addr;
ip->ip_dst = saidx->dst.sin.sin_addr;
ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos);
ip_fillid(ip);
ip_fillid(ip, V_ip4_ipsec_random_id);
break;
#endif /* INET */
#ifdef INET6

View file

@ -5952,7 +5952,7 @@ ipf_updateipid(fr_info_t *fin)
id = (u_short)sum;
ip->ip_id = htons(id);
} else {
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
id = ntohs(ip->ip_id);
if ((fin->fin_flx & FI_FRAG) != 0)
(void) ipf_frag_ipidnew(fin, (u_32_t)id);

View file

@ -5117,7 +5117,7 @@ ipf_nat_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags)
}
ip = MTOD(m, ip_t *);
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
s2 = ntohs(ip->ip_id);
s1 = ip->ip_len;
@ -5560,7 +5560,7 @@ ipf_nat_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags)
}
ip = MTOD(m, ip_t *);
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
sum1 = ntohs(ip->ip_len);
ip->ip_len = ntohs(ip->ip_len);
ip->ip_len += fin->fin_plen;

View file

@ -520,7 +520,7 @@ nat64_init_ip4hdr(const struct ip6_hdr *ip6, const struct ip6_frag *frag,
ip->ip_ttl -= IPV6_HLIMDEC;
ip->ip_sum = 0;
ip->ip_p = (proto == IPPROTO_ICMPV6) ? IPPROTO_ICMP: proto;
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
if (frag != NULL) {
ip->ip_off = htons(ntohs(frag->ip6f_offlg) >> 3);
if (frag->ip6f_offlg & IP6F_MORE_FRAG)
@ -845,7 +845,7 @@ nat64_icmp_reflect(struct mbuf *m, uint8_t type,
oip->ip_len = htons(n->m_pkthdr.len);
oip->ip_ttl = V_ip_defttl;
oip->ip_p = IPPROTO_ICMP;
ip_fillid(oip);
ip_fillid(oip, V_ip_random_id);
oip->ip_off = htons(IP_DF);
oip->ip_src = ip->ip_dst;
oip->ip_dst = ip->ip_src;

View file

@ -1900,7 +1900,7 @@ pfsync_sendout(int schedswi, int c)
len -= sizeof(union inet_template) - sizeof(struct ip);
ip->ip_len = htons(len);
ip_fillid(ip);
ip_fillid(ip, V_ip_random_id);
break;
}
#endif

View file

@ -3527,7 +3527,7 @@ pf_translate_af(struct pf_pdesc *pd)
ip4->ip_hl = hlen >> 2;
ip4->ip_tos = pd->tos;
ip4->ip_len = htons(hlen + (pd->tot_len - pd->off));
ip_fillid(ip4);
ip_fillid(ip4, V_ip_random_id);
ip4->ip_ttl = pd->ttl;
ip4->ip_p = pd->proto;
ip4->ip_src = pd->nsaddr.v4;
@ -3630,7 +3630,7 @@ pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd,
ip4->ip_v = IPVERSION;
ip4->ip_hl = sizeof(*ip4) >> 2;
ip4->ip_len = htons(sizeof(*ip4) + pd2->tot_len - olen);
ip_fillid(ip4);
ip_fillid(ip4, V_ip_random_id);
ip4->ip_off = htons(IP_DF);
ip4->ip_ttl = pd2->ttl;
if (pd2->proto == IPPROTO_ICMPV6)

View file

@ -2254,7 +2254,7 @@ pf_scrub(struct pf_pdesc *pd)
pd->act.flags & PFSTATE_RANDOMID && !(h->ip_off & ~htons(IP_DF))) {
uint16_t ip_id = h->ip_id;
ip_fillid(h);
ip_fillid(h, V_ip_random_id);
h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_id, h->ip_id, 0);
}
#endif