From nobody Thu Sep 24 21:47:03 2026 Received: from mail-ua2-f12.google.com (mail-ua2-f12.google.com [74.125.226.204]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6EFA93D953C for ; Sun, 20 Sep 2026 05:47:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.226.204 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789883242; cv=none; b=nbNF7UqRTcJWrRyCSFvSv39MmxU5BDgrt44VAXRJUVRps3a2LE3Ly36YtQIfwzBoZSmdonGHgOjSAhCjFmLsriPgQS6ojKecdgR5HTThafhsgm5AspknGtg6lDeHJGfmSj5soZpIpp8Ybp0wvjRty5ApO8+iUbgoDNt6Lw5796A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789883242; c=relaxed/simple; bh=XwLRDqoGDPXb6K5PWgL1M7DdV69+VMAJJyWkcb0+NUo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ApPJIjHYiFTiZEwUWWITKDOb7xAfxBH2odtEtQujxVvhiBnGYPem2t0k8qYka+cp7ENd4OtHfFRFjYYCjOh2rqxoWa5/5ftyeOSyq0F41XdONhaQBFExw4Bp4C5pZFCsO4yqfT1yyra0XOCT+RiV4oF6BeaOmj2lR0dyQn/H6E8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=O8amkPY2; arc=none smtp.client-ip=74.125.226.204 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="O8amkPY2" Received: by mail-ua2-f12.google.com with SMTP id a1e0cc1a2514c-97e7c7bab8eso1032407241.0 for ; Sat, 19 Sep 2026 22:47:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789883239; x=1790488039; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=83+plyF4HOp6Tc6yDaOIUjOh/yxbiFagCuYc/tYX0qs=; b=O8amkPY2Esj2W5BnE6vPGOjoGsQWoAqT5FK7u/mJvfO+xn7KdJfg7QTD14/1F/VxVZ 5AhQVDPtOS6Z1DYVxgMEmfUtQ2T9A2zOgx6i/qq8Jpn7YTj+Wx0O+TSZI34yyKmO5eSq Ua8/4r7XVIrLOSIJDa3v/8loPoeQ2tUkwpvSZQHgn/k2ALLvBQT3SY48ioD5rx3yOqVx TvHIB2YLLuV8vPNFnb3F71jECq4R+n6ht2kyalDfHZwQZdWjrWKMCaa1PDjeXu6VbJob waVy0azEEBPHBwDbZi5oValmfbh6oFCS7FvqPOre0d8UvTcT9AHA3zvbDN2lhj2FdBcK LBKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789883239; x=1790488039; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=83+plyF4HOp6Tc6yDaOIUjOh/yxbiFagCuYc/tYX0qs=; b=UWT3q/UWMWcSrySobqzSuEm60VJAH9KiFrlVYJ6m++RpajNMFp7Ho5BFB3FF9br/tP DhbF4gZax7WDdRA6Vk6G6NAw0+VHP4/j/EKVBbI/MIqcAcAU6bd9ai0UanMTTkrV7FTB vH1I93IVfnklM5gLpANqFjU19/2SVQL5lPFoJLT04/gEV8gtKEyR2NNol55OzBqCKPUk xk7QuJjWbPXMVyC5fuetBOymLqTejadMC2oRaJpbrCuR8ruZwIvxNosypWLuRVZBIXuW 9mSUuhmE8LKubFPe8O2/yGRBD3jR5aDmNS82H/4suZoL7rwWXPf9TrnoJKKbmxbTm8Oh Vzpg== X-Forwarded-Encrypted: i=1; AKwUvBzh3HcHxSdoD+dNQ2mG/1SZG6WuE3kbEXkehSXU8hzoe3Gf8o3aAyt636Ll0xKWLjLgVNXB1zOHRBY3i+I=@vger.kernel.org X-Gm-Message-State: AFuF++l42BRevWevFCdXXpmm0xsmu4ZKITT5Wxg1/29ehgAcTRelSEjQ RVrsj/F9p/vKFbdYLIXPwx6vfUGGt/jvNEWngcvVOsJdBWshvXab01W3 X-Gm-Gg: AYBFou3T8Ee2yayumVQFSHiXLXBPyUOtzxjhqfm52HR97pNTJdMXVoWyIn785W2p8Pg PE8qR+ygp35sp+RMalTatG4FCPZKqirtTy8/klXWZC117hpf8SFAa1kC6m8JseawSRYkFQB1Exx sb/RHteidJQ2HOXsY7ZaYd8JjTG2xHSs+t6bYw38Y1Lli7mDuyvav1572od83b1T8WS6UI7P7Bp qoBfQQSex7tNXPdnxyWoYk2IqxF6c51ZnQ7alObtwgdTu/UFxvfR7kmqHUEU8i78HlI1NvMhWGV 4sybIbp5FLVgvNWbLzGaqFjG/uCOY/hYXPqEEApEDgMBsGJdU6uI4vCpqcSd0RRcbiVbXpy9gyv dC5g8A/iUsVndTZx5j+YSZlZTq/HJz3m68Jkykq3igJJUU+O3XU4kaoBMBUrMX42YTQWRMjVjHY NkWfYCl7BrMh62myuv+FtUTDQ4NLPJPq8faquRcwJD2rVw3gFaAP8TJlIaG4G3/JY= X-Received: by 2002:a05:6102:e0e:b0:7a7:b6a:b022 with SMTP id ada2fe7eead31-7a70b6ab56dmr924527137.22.1789883239412; Sat, 19 Sep 2026 22:47:19 -0700 (PDT) Received: from adriano ([190.215.95.120]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-7a6aa41dac0sm4521732137.2.2026.09.19.22.47.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 19 Sep 2026 22:47:19 -0700 (PDT) From: Adriano Cordova To: Simon Horman , Julian Anastasov , Pablo Neira Ayuso Cc: Florian Westphal , netfilter-devel@vger.kernel.org, lvs-devel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 nf-next 1/3] ipvs: add flags for per-service secure TCP state table Date: Sun, 20 Sep 2026 02:47:06 -0300 Message-ID: <20260920054708.988867-2-adrianox@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260920054708.988867-1-adrianox@gmail.com> References: <20260920054708.988867-1-adrianox@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add IP_VS_SVC_F_SECURE_TCP to mark a virtual service for the DoS-hardened TCP connection state table, and IP_VS_CONN_F_SECURE_TCP to carry that capability on a connection so the TCP state machine can select the hardened table for it. The service flag is 0x0100: bits 0x0008/0x0010/0x0020 are the scheduler bits, so leave 0x0040/0x0080 free for schedulers. Set IP_VS_CONN_F_SECURE_TCP on every connection bound to a destination whose service carries IP_VS_SVC_F_SECURE_TCP. Signed-off-by: Adriano Cordova --- Changes in v2: - Let IP_VS_SVC_F_SECURE_TCP be 0x0100, leaving 0x0040/0x0080 free for scheduler flags. - Let IP_VS_CONN_F_SECURE_TCP be (1 << 17) and drop it from IP_VS_CONN_F_BACKUP_MASK. - Set the flag in ip_vs_bind_dest() instead of at every ip_vs_conn_new() call site. Changes in v3: - Merge the uapi flag definition and the ip_vs_bind_dest() stamping into a single patch. (no changes since v3) include/uapi/linux/ip_vs.h | 2 ++ net/netfilter/ipvs/ip_vs_conn.c | 4 ++++ net/netfilter/ipvs/ip_vs_core.c | 3 +++ 3 files changed, 9 insertions(+) diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index 2c37c6ac7525..ade170109ff4 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -27,6 +27,7 @@ =20 #define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */ #define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */ +#define IP_VS_SVC_F_SECURE_TCP 0x0100 /* use the hardened TCP table */ =20 /* * IPVS sync daemon states @@ -105,6 +106,7 @@ =20 /* Flags that are not sent to backup server start from bit 16 */ #define IP_VS_CONN_F_NFCT (1 << 16) /* use netfilter conntrack */ +#define IP_VS_CONN_F_SECURE_TCP (1 << 17) /* use the hardened TCP table */ =20 /* Connection flags from destination that can be changed by user space */ #define IP_VS_CONN_F_DEST_MASK (IP_VS_CONN_F_FWD_MASK | \ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_con= n.c index 6fa3e1dc534c..0b465298b64b 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1090,6 +1090,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_d= est *dest) { unsigned int conn_flags; __u32 flags; + struct ip_vs_service *svc; =20 /* if dest is NULL, then return directly */ if (!dest) @@ -1102,6 +1103,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_d= est *dest) if (cp->protocol !=3D IPPROTO_UDP) conn_flags &=3D ~IP_VS_CONN_F_ONE_PACKET; flags =3D cp->flags; + svc =3D rcu_dereference(dest->svc); + if (svc && (svc->flags & IP_VS_SVC_F_SECURE_TCP)) + flags |=3D IP_VS_CONN_F_SECURE_TCP; /* Bind with the destination and its corresponding transmitter */ if (flags & IP_VS_CONN_F_SYNC) { /* Synced conns are hashed, so they can not get this flag */ diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_cor= e.c index ba0957798bad..aa8a3a964ffd 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -784,6 +784,9 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_bu= ff *skb, IP_VS_CONN_F_ONE_PACKET : 0; union nf_inet_addr daddr =3D { .all =3D { 0, 0, 0, 0 } }; =20 + if (svc->flags & IP_VS_SVC_F_SECURE_TCP) + flags |=3D IP_VS_CONN_F_SECURE_TCP; + /* create a new connection entry */ IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__); { --=20 2.51.0 From nobody Thu Sep 24 21:47:03 2026 Received: from mail-vs2-f41.google.com (mail-vs2-f41.google.com [74.125.227.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C95093DA5AB for ; Sun, 20 Sep 2026 05:47:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789883245; cv=none; b=ojZUDKxXgEyjyKsPJFu8KaFzqXE23Qq1KhEY0Q5hXHQdiaoJAPs9RhRIYzKNboJ7Df1dtf3FA7M3xzVXZXvHPHee6y4mfHHYAHEAuhUvFsQm8Tjp4z3mf+Octs1nCC2s2JCeLgsYMIV4uRIZzw0FPvr7fwBcEb0TubCUFtB6YO0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789883245; c=relaxed/simple; bh=vGxo+U1sJMCVsq3sDI08zPDza2WpfMA+ZZBuxg0Rgp4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PM1UvQMj2qE6oWX8ryB5+NSZbX1D6ENWVyBIAqheHt4HOk/gRYBlMR0Wi0d5oAIbiTT9or+Tcrqo24C3Ftx5G4W+3YV4E9BR+C05dazWmaOcMG1c2ml1HSj+H99vlrT7aGq+V9KWJVmy808RoIirf3Q3dkunsoZVzlj7s7N633U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=NrWAtjzW; arc=none smtp.client-ip=74.125.227.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NrWAtjzW" Received: by mail-vs2-f41.google.com with SMTP id ada2fe7eead31-78565b4db77so1077364137.0 for ; Sat, 19 Sep 2026 22:47:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789883242; x=1790488042; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=+xdh7oWEQ9m8Qy+E3UVjKRIovBOhuQDCeQrchOYtk/I=; b=NrWAtjzW6crMVPACP5AVxPI3gHWW7SYC8jMg68W9Rk36SG8ecUGin3pHSbLmpDQjtD XsqGKITCAwaQmYXY6iwtFsmlgEz4lfCzErRqqSN/hH9iPbUCyAIvREnGpnAxtGFAdrtn zS8jF6UBcK33ogi2UF2F3RAs6diM5HOoePLdeROPilTTo7YK/9AqLzOiI50XnCLSLtR7 Fj+2EVjiYvhpFaj08Vlpi4tp5L5AQQNt2CQj3sMkTBdPzYcJtbQf+xLlJcbss2B2tkbl phzWY2YxOZbilgHjXBB6UjnF5QkAFjvhqxbN+dyqkQCc9d0tdWT2SDTYmnA18C1MXmha 9oDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789883243; x=1790488043; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=+xdh7oWEQ9m8Qy+E3UVjKRIovBOhuQDCeQrchOYtk/I=; b=K/O6CuTKRV7OcKe/LqlVTV3M4Asb8kKaVBQag3MdDOmYXWhzxpxVI3excIs2dnAsJE 1QI9McAX8lQma2MX9YeBNEBnL/EXEkpxjEZFY68fFdld2EImcelrSvreI8H9ukTCn0SG oNy9UKePjTyXc+Pu8p8n0pDT76tZKV2CK0bU3dSammusqUPijXpsKG7ED8W8zLwHSn0z ZBHQgA9OYzHhryZzTEoiyh9rqAbXl9JDZAeDBF9VgzuyU601mFGFIZ/znb2WO4/yHvu7 BsKVWx2aG8lwz0OYfAyssQs87NXMxCLmQdWYnRdwIRuJl0A7GIU2usqSDOpc0eR2AzFN qt/w== X-Forwarded-Encrypted: i=1; AKwUvBxvRwNgz3jddRFWd4xgqelOIrgQ2PDQue/4hj+KnmbEKw3AS/KieDwSWBt7vnpi8UsOkw8XU8jEibBYMDk=@vger.kernel.org X-Gm-Message-State: AFuF++lot+M8DHU3LFb7BJIgmMk6iPSzduvO2hvaWv36XA3/att2Dwgb WFNXlimaK2XPAtCZJsyUQ/q+n0WoPtR+6y0csa3QmLcpwDruk2XEQ8HOAjfh0OQI X-Gm-Gg: AYBFou29WYfcNuxXk4hMMLweGvkAropOYVu8U4o64HH2NfhmYRtG5njU1tvKG7yC/Ti 6dAk0vG2HYjZ5gQVftp5xGh6v8dsb1EGB2Ry72mIxCaKplDsILflPIzCF5b/tnrsR/Em3KWNtyd h2RsIS2pFlITSuRz7nolNuGcy/xHp4TQC2Kl5go4Q91mE4xGW/y+EWiwP01uZdp6bsR6RcDjl18 9+3O079d56Iav/mcjm0FN2mxlLUiq6/ef6F0HI8J2VFtG25TsAL1Bq5XhEBsnGABeZj/2i1agkJ amw8BxYzDVzKxKrbbFYnDndo9wpfWBEEfg97KbuxN6g/zCAeXnm69ePEWSNjq3xiOvMg14YzWof Vf6dzu8hBGDc3TSvRyzGBR1eBERfhzZoiHZA2WUjOvWlJ/04Nw/o/XzlbDKA4IRkS4F2px2wPuu d4upu7dvY7DTvsGAEd6J9sA2dx1FHbGyajzAw9sfZMcjxap+6USvA+5PfkkB73Rj8= X-Received: by 2002:a05:6102:158c:b0:7a5:2427:ecc1 with SMTP id ada2fe7eead31-7a55ce7d127mr3164716137.19.1789883242711; Sat, 19 Sep 2026 22:47:22 -0700 (PDT) Received: from adriano ([190.215.95.120]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-7a6aa41dac0sm4521732137.2.2026.09.19.22.47.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 19 Sep 2026 22:47:21 -0700 (PDT) From: Adriano Cordova To: Simon Horman , Julian Anastasov , Pablo Neira Ayuso Cc: Florian Westphal , netfilter-devel@vger.kernel.org, lvs-devel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 nf-next 2/3] ipvs: tcp: enable per-connection secure_tcp in state machine Date: Sun, 20 Sep 2026 02:47:07 -0300 Message-ID: <20260920054708.988867-3-adrianox@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260920054708.988867-1-adrianox@gmail.com> References: <20260920054708.988867-1-adrianox@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Let set_tcp_state select tcp_states_dos when the connection carries IP_VS_CONN_F_SECURE_TCP, otherwise keep using the global pd->tcp_state_table (the netns default / or if nomem). Signed-off-by: Adriano Cordova --- (no changes since v1) net/netfilter/ipvs/ip_vs_proto_tcp.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_v= s_proto_tcp.c index fec0e8b47b71..3b9a2c8e9a52 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -451,11 +451,10 @@ static void tcp_timeout_change(struct ip_vs_proto_dat= a *pd, int flags) int on =3D (flags & 1); /* secure_tcp */ =20 /* - ** FIXME: change secure_tcp to independent sysctl var - ** or make it per-service or per-app because it is valid - ** for most if not for all of the applications. Something - ** like "capabilities" (flags) for each object. - */ + * This remains the netns-wide default / global floor (e.g. when + * memory pressure kicks in). Per-service hardening is now carried + * by IP_VS_CONN_F_SECURE_TCP on each connection (set_tcp_state). + */ pd->tcp_state_table =3D (on ? tcp_states_dos : tcp_states); } =20 @@ -479,6 +478,7 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs= _conn *cp, int state_idx; int new_state =3D IP_VS_TCP_S_CLOSE; int state_off =3D tcp_state_off[direction]; + const struct tcp_states_t *table; =20 /* * Update state offset to INPUT_ONLY if necessary @@ -496,8 +496,10 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_v= s_conn *cp, goto tcp_state_out; } =20 - new_state =3D - pd->tcp_state_table[state_off+state_idx].next_state[cp->state]; + table =3D pd->tcp_state_table; + if (cp->flags & IP_VS_CONN_F_SECURE_TCP) + table =3D tcp_states_dos; + new_state =3D table[state_off + state_idx].next_state[cp->state]; =20 tcp_state_out: if (new_state !=3D cp->state) { --=20 2.51.0 From nobody Thu Sep 24 21:47:03 2026 Received: from mail-vs2-f41.google.com (mail-vs2-f41.google.com [74.125.227.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B8F83DB314 for ; Sun, 20 Sep 2026 05:47:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789883251; cv=none; b=IR0VYU7FGYpEw8lQtg6AGB0d23YTtaxTSSus50ithSjMzwBGkXCi7LuTG9FHakG6bwpfdb857YjJWp/hLZ4pGlV9gyOyEseVLqu3n1tochRXM7mrK7UKzVvpfPX9C+nfD6b9uqdRRIXCfAo1BJo7ZF2dXVDwt4KT3b1nGayKcog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789883251; c=relaxed/simple; bh=/lkdrpPYNixWzk/J6k3UBYHWwbDE2CaQgDAX0Du2Xk0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LDwOFUZnygL/96nkEPug5EJKGtzXHkw2l06wuO2sVFQOFIDhhVfCWFfUDrACAGy6Op9NI938uqefE5JwE9gFWCEeUaqq2KQZPEmoUU37Zu6J9WrjJ4NTBEDOYs4PvTwrDr4x3GRg6D2S02uHlzif1WTHjRbcOq7VqClj66MyTos= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nsa/KyL9; arc=none smtp.client-ip=74.125.227.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nsa/KyL9" Received: by mail-vs2-f41.google.com with SMTP id 71dfb90a1353d-5c67e5168ceso620295e0c.3 for ; Sat, 19 Sep 2026 22:47:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789883246; x=1790488046; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=3OY6EE4qyGSCZusvaZRNrAhhc97HgRfpXDfasp9UVKQ=; b=nsa/KyL9pK1eWttDR1rs1nNjHsZuGtWyjPUyK9ee5E+vMNLBUsMEwSKiWHjYHU5EGG gxdeNNzTcnrvhg7GU7ClScTiqN3QP4bjG0BpWkbxvBuKAIDOb+5KbpfMmHEjiQXc2YCg iQnF0JOF9ZIfUhw1Tztt+WszNEBY9kdX4oLCcN4v0SWVIKYLVlDA+k1c+rCoUrsF/Q1v tkIkj9iqd2LUrwdNtYqw7dhIWLn78TZBeZuU06ILA3bmMxmEKJk3J6WD6RbT26xJ0G1o FGWmFjrO02qaGQlATMDImm0amELX1td7dCXNKTMVr3QsK4nFXZuoZeTr+uP+Vh2TpIrG cdHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789883246; x=1790488046; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=3OY6EE4qyGSCZusvaZRNrAhhc97HgRfpXDfasp9UVKQ=; b=c7QKeMk+fxTkSl9ry/TvpivWjS4OI68nWV2oJjxhVkkWzUZg1dv9YUPRidYQ1UYzlV 9QqphYyoqFLTTOMQouq4IfiQdjshlcfBUPEBMUIkeUiTeA94noviANeGKCl0GHJuitHs WTBbiQpuBeSx1aLSwiH/ZfBb7/v7/a9ff4ZHtA0vkIVVErIvMTO44CnBlmiFOCRiDmsv lXRFamBv3XywUd7uLzdoHjrwmPOt6Asm2CDRl0Y6oSM9aOHcAyCalmgL4Fg7ngp1dfWj bU1aFS+2VNx47xj1Euyh0JkLnLAfKBMz1rtcq/V1s5VRVGziL0D3+VkRSgd2JIPrjSLq gdfA== X-Forwarded-Encrypted: i=1; AKwUvBxNhdwKr6WXFrNxQ9FBoes2YGH7dPzBnHa6H3sadPXSReNnpJzepqCqjcAjzB/+SbAadhcKRqZ13/pR7do=@vger.kernel.org X-Gm-Message-State: AFuF++l7U3zG/qiyGyJQe4z7EdwauaD2WkX7kRG8LW1yGWi4mONCxJqB sWNzAPUmAa2sNJTAUGGNvctMhzoJQzVwDMrEmkaQqlcwprY5Wc1srDAtkdQWUHlx X-Gm-Gg: AYBFou2blDqk/v48ghN4IrwH+EXRQ6BpD+15ekuNVtxzuOihwm40+QmJRMh295Ueh65 pdFyU7W4QRDJT1jGWsbpKz1/pO2Nxu4Ix1DLnOjFYIAETQ0UxL7H41zNNf+BkA6cpSOghUVyqIY xrcH+Wjs65ftAYAD0UnMNPvVcm5KgrbgpzAfSRRrkHLc+ZgT2A5J82gM4hoi/pBryQOHUNrRSfD X6M9hG/FblUE4fvjYxuXZn2oZcJi0pdMKSKsQPojFq4lwYr+n4WaP8MJR7dL6w6mFqjh9Zj1os/ HtkqXGs0fMAA40qi9g441sX6LkfwHy4DcQRIeYGhzgK2YlfuO3//w2Su/LV//SGsvOoGLYq54vG YL2B3utivS+JLZX8+sQAefLTKhdq+2AX05DU7k1YgUinb4j/puj8DwXA3u53uDZ0gMQSmbJg43q PGik4a4e38Ir9Sh0OFjDQusa4fv6+81vHT3UDP7VZR0Dr3R984xIZep/w9b7PahlY= X-Received: by 2002:a05:6102:5345:b0:7a7:195a:1b23 with SMTP id ada2fe7eead31-7a7195a1f91mr943555137.29.1789883246218; Sat, 19 Sep 2026 22:47:26 -0700 (PDT) Received: from adriano ([190.215.95.120]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-7a6aa41dac0sm4521732137.2.2026.09.19.22.47.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 19 Sep 2026 22:47:25 -0700 (PDT) From: Adriano Cordova To: Simon Horman , Julian Anastasov , Pablo Neira Ayuso Cc: Florian Westphal , netfilter-devel@vger.kernel.org, lvs-devel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 nf-next 3/3] selftests: netfilter: ipvs: add per-service secure_tcp test Date: Sun, 20 Sep 2026 02:47:08 -0300 Message-ID: <20260920054708.988867-4-adrianox@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260920054708.988867-1-adrianox@gmail.com> References: <20260920054708.988867-1-adrianox@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Two services share a VIP, one carrying IP_VS_SVC_F_SECURE_TCP. A bare SYN+ACK suffices to test the state machine: the normal service reaches ESTABLISHED, but the secure one stays in SYN_RECV. Assisted-by: opencode:deepseek-flash Signed-off-by: Adriano Cordova --- Changes in v2: - send probes with TTL=3D1 instead of an nft drop, and fix Sashiko comments. Changes in v4: - Check the return value of setsockopt(IP_HDRINCL), sendto() and mnl_socket_bind(), and fail do_add() when IPVS does not reply. - Check inet_pton(), initialize fam and the parsed addresses, and verify the flags attribute length before copying it (Sashiko). .../testing/selftests/net/netfilter/Makefile | 6 + .../selftests/net/netfilter/gen_tcp_probe.c | 158 ++++++++ .../net/netfilter/ipvs_secure_tcp.sh | 153 ++++++++ .../net/netfilter/ipvs_secure_tcp_mln.c | 337 ++++++++++++++++++ 4 files changed, 654 insertions(+) create mode 100644 tools/testing/selftests/net/netfilter/gen_tcp_probe.c create mode 100755 tools/testing/selftests/net/netfilter/ipvs_secure_tcp.sh create mode 100644 tools/testing/selftests/net/netfilter/ipvs_secure_tcp_m= ln.c diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing= /selftests/net/netfilter/Makefile index df3c20c90f5d..f88bf69cc874 100644 --- a/tools/testing/selftests/net/netfilter/Makefile +++ b/tools/testing/selftests/net/netfilter/Makefile @@ -22,6 +22,7 @@ TEST_PROGS :=3D \ conntrack_tcp_unreplied.sh \ conntrack_vrf.sh \ ipvs.sh \ + ipvs_secure_tcp.sh \ nf_conntrack_packetdrill.sh \ nf_nat_edemux.sh \ nft_audit.sh \ @@ -52,6 +53,8 @@ TEST_GEN_FILES =3D \ connect_close \ conntrack_dump_flush \ conntrack_reverse_clash \ + gen_tcp_probe \ + ipvs_secure_tcp_mln \ nf_queue \ sctp_collision \ udpclash \ @@ -62,6 +65,9 @@ include ../../lib.mk $(OUTPUT)/nf_queue: CFLAGS +=3D $(MNL_CFLAGS) $(OUTPUT)/nf_queue: LDLIBS +=3D $(MNL_LDLIBS) =20 +$(OUTPUT)/ipvs_secure_tcp_mln: CFLAGS +=3D $(MNL_CFLAGS) +$(OUTPUT)/ipvs_secure_tcp_mln: LDLIBS +=3D $(MNL_LDLIBS) + $(OUTPUT)/conntrack_dump_flush: CFLAGS +=3D $(MNL_CFLAGS) $(OUTPUT)/conntrack_dump_flush: LDLIBS +=3D $(MNL_LDLIBS) $(OUTPUT)/udpclash: LDLIBS +=3D -lpthread diff --git a/tools/testing/selftests/net/netfilter/gen_tcp_probe.c b/tools/= testing/selftests/net/netfilter/gen_tcp_probe.c new file mode 100644 index 000000000000..185c3b1146dc --- /dev/null +++ b/tools/testing/selftests/net/netfilter/gen_tcp_probe.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Send a TCP SYN then a TCP ACK (no SYN-ACK, no data) to the VIP. + * IPVS's TCP state machine only inspects SYN/FIN/ACK/RST bits, so this + * exercises the INPUT-direction state transition: + * + * SYN: NONE -> SYN_RECV + * ACK: SYN_RECV -> ESTABLISHED (tcp_states, normal) + * SYN_RECV -> SYN_RECV (tcp_states_dos, secure_tcp) + * + * A TTL of 1 keeps the packets from reaching the real server: IPVS updates + * the connection state before forwarding, then the packet expires and an + * ICMP time-exceeded is sent back to us. + * + * Requires CAP_NET_RAW. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline uint16_t csump(const void *data, size_t len) +{ + const uint8_t *p =3D data; + uint32_t sum =3D 0; + + while (len > 1) { + uint16_t w; + + memcpy(&w, p, sizeof(w)); + sum +=3D w; + p +=3D 2; + len -=3D 2; + } + if (len) { + uint16_t w =3D 0; + + memcpy(&w, p, 1); + sum +=3D w; + } + while (sum >> 16) + sum =3D (sum & 0xffff) + (sum >> 16); + return ~sum; +} + +static int send_seg(int fd, const struct in_addr *sip, uint16_t sport, + const struct in_addr *dip, uint16_t dport, + uint32_t seq, int syn, int ack) +{ + struct { + struct iphdr ip; + struct tcphdr tcp; + } pkt =3D { }; + struct iphdr *ip =3D &pkt.ip; + struct tcphdr *tcp =3D &pkt.tcp; + struct sockaddr_in dst; + + ip->version =3D 4; + ip->ihl =3D 5; + ip->tot_len =3D htons(sizeof(pkt)); + ip->id =3D htons((uint16_t)(seq & 0xffff)); + ip->ttl =3D 1; + ip->protocol =3D IPPROTO_TCP; + ip->saddr =3D sip->s_addr; + ip->daddr =3D dip->s_addr; + + tcp->source =3D sport; + tcp->dest =3D dport; + tcp->seq =3D htonl(seq); + tcp->ack_seq =3D htonl(seq + 1); + tcp->doff =3D 5; + if (syn) + tcp->syn =3D 1; + if (ack) + tcp->ack =3D 1; + tcp->window =3D htons(1024); + + ip->check =3D csump(ip, sizeof(struct iphdr)); + /* pseudo header for TCP checksum */ + { + uint8_t ph[12]; + uint8_t tcpbuf[12 + sizeof(struct tcphdr)]; + + memcpy(ph, &ip->saddr, 4); + memcpy(ph + 4, &ip->daddr, 4); + ph[8] =3D 0; + ph[9] =3D IPPROTO_TCP; + ph[10] =3D (sizeof(struct tcphdr) >> 8) & 0xff; + ph[11] =3D sizeof(struct tcphdr) & 0xff; + + memcpy(tcpbuf, ph, 12); + memcpy(tcpbuf + 12, tcp, sizeof(struct tcphdr)); + tcp->check =3D csump(tcpbuf, sizeof(tcpbuf)); + } + + memset(&dst, 0, sizeof(dst)); + dst.sin_family =3D AF_INET; + dst.sin_addr =3D *dip; + dst.sin_port =3D dport; + if (sendto(fd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, + sizeof(dst)) < 0) { + perror("sendto"); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + struct in_addr sip =3D { }, dip =3D { }; + uint16_t sport, dport; + int fd, one =3D 1; + uint32_t seq =3D 0x12345678; + + if (argc !=3D 5) { + fprintf(stderr, "usage: %s \n", + argv[0]); + return 2; + } + if (inet_pton(AF_INET, argv[1], &sip) !=3D 1 || + inet_pton(AF_INET, argv[3], &dip) !=3D 1) { + fprintf(stderr, "bad address\n"); + return 2; + } + sport =3D htons((uint16_t)atoi(argv[2])); + dport =3D htons((uint16_t)atoi(argv[4])); + + fd =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (fd < 0) { + perror("raw socket"); + return 1; + } + if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) { + perror("setsockopt"); + close(fd); + return 1; + } + + if (send_seg(fd, &sip, sport, &dip, dport, seq, 1, 0) < 0) { + close(fd); + return 1; + } + usleep(100000); + if (send_seg(fd, &sip, sport, &dip, dport, seq + 1, 0, 1) < 0) { + close(fd); + return 1; + } + + close(fd); + return 0; +} diff --git a/tools/testing/selftests/net/netfilter/ipvs_secure_tcp.sh b/too= ls/testing/selftests/net/netfilter/ipvs_secure_tcp.sh new file mode 100755 index 000000000000..4d250bcbb2cf --- /dev/null +++ b/tools/testing/selftests/net/netfilter/ipvs_secure_tcp.sh @@ -0,0 +1,153 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Runtime test for per-service secure_tcp (IP_VS_SVC_F_SECURE_TCP). +# +# Sets up the same 3-namespace topology as ipvs.sh +# but checks the TCP state machine, not data forwarding. Two +# identical TCP services are added on the same VIP on different ports, +# one is marked secure_tcp, the other is not. For each a bare SYN is +# followed by a bare ACK (no SYN-ACK / no data). IPVS classifies the +# connection from the flag bits: +# * normal service: SYN -> SYN_RECV, ACK -> ESTABLISHED +# * secure_tcp service: SYN -> SYN_RECV, ACK -> SYN_RECV +# This test checks that this is the case via `ipvsadm -Lnc`. +# +# Requires root, netns, ipvsadm, and the built helpers +# ipvs_secure_tcp_mln and gen_tcp_probe. + +source lib.sh + +ret=3D0 +readonly vip=3D"207.175.44.110" +readonly gip=3D"10.0.0.1" +readonly dip=3D"172.16.0.1" +readonly rip=3D"172.16.0.2" +readonly cip=3D"10.0.0.2" +readonly sip=3D"10.0.0.3" +readonly port_secure=3D8081 +readonly port_plain=3D8080 + +GREEN=3D'\033[0;92m' +RED=3D'\033[0;31m' +NC=3D'\033[0m' + +checktool "ipvsadm -v" "run test without ipvsadm" + +setup() { + setup_ns ns0 ns1 ns2 + + ip link add veth01 netns "${ns0}" type veth peer name veth10 netns "${ns1= }" + ip link add veth02 netns "${ns0}" type veth peer name veth20 netns "${ns2= }" + ip link add veth12 netns "${ns1}" type veth peer name veth21 netns "${ns2= }" + + ip netns exec "${ns0}" ip link set veth01 up + ip netns exec "${ns0}" ip link set veth02 up + ip netns exec "${ns0}" ip link add br0 type bridge + ip netns exec "${ns0}" ip link set veth01 master br0 + ip netns exec "${ns0}" ip link set veth02 master br0 + ip netns exec "${ns0}" ip link set br0 up + ip netns exec "${ns0}" ip addr add "${cip}/24" dev br0 + + ip netns exec "${ns1}" ip link set veth10 up + ip netns exec "${ns1}" ip addr add "${gip}/24" dev veth10 + ip netns exec "${ns1}" ip link set veth12 up + ip netns exec "${ns1}" ip addr add "${dip}/24" dev veth12 + ip netns exec "${ns1}" ip link set lo up + ip netns exec "${ns1}" ip addr add "${vip}/32" dev lo:1 + ip netns exec "${ns1}" sysctl -qw net.ipv4.ip_forward=3D1 + + ip netns exec "${ns2}" ip link set veth20 up + ip netns exec "${ns2}" ip addr add "${sip}/24" dev veth20 + ip netns exec "${ns2}" ip link set veth21 up + ip netns exec "${ns2}" ip addr add "${rip}/24" dev veth21 + + ip netns exec "${ns2}" ip addr add "${vip}/32" dev lo:1 + + ip netns exec "${ns0}" ip route add "${vip}/32" via "${gip}" dev br0 + + # load ipvs, then the rr scheduler (separate calls: modprobe treats + # the second name as a module parameter, not a second module) + ip netns exec "${ns1}" modprobe ip_vs + ip netns exec "${ns1}" modprobe ip_vs_rr + + sleep 1 +} + +cleanup() { + cleanup_all_ns +} + +# State of the connection to the VIP:port, from `ipvsadm -Lnc`. +# Fields: pro expire state source virtual destination +conn_state() { + local vport=3D$1 + ip netns exec "${ns1}" ipvsadm -Lnc 2>/dev/null | + awk -v vt=3D"${vip}:${vport}" '$5=3D=3Dvt { print $3; exit }' +} + +assert_state() { + local port=3D$1 want=3D$2 + local got + got=3D"$(conn_state "$port")" + echo " vip ${vip}:${port}: state=3D${got:-?}" + if [ "${got:-}" !=3D "$want" ]; then + echo -e "${RED}FAIL${NC}: vip ${vip}:${port} expected state" \ + "${want}, got ${got:-none}" + ret=3D1 + fi +} + +test_secure() { + local bin probe + + # Register the two services (secure_tcp on the secure port) + bin=3D"$(pwd)/ipvs_secure_tcp_mln" + probe=3D"$(pwd)/gen_tcp_probe" + ip netns exec "${ns1}" "$bin" add "${vip}" "${port_secure}" secure + ip netns exec "${ns1}" "$bin" add "${vip}" "${port_plain}" plain + + # Add a real server to both services. Use NAT (-m): in DR the conn gets + # IP_VS_CONN_F_NOOUTPUT, which makes the client ACK an INPUT_ONLY event + # and even tcp_states_dos promotes to ESTABLISHED, hiding the difference. + ip netns exec "${ns1}" ipvsadm -a -m -t "${vip}:${port_secure}" -r "${rip= }:${port_secure}" + ip netns exec "${ns1}" ipvsadm -a -m -t "${vip}:${port_plain}" -r "${rip}= :${port_plain}" + + # verify the flag was actually set + local got + got=3D"$(ip netns exec "${ns1}" "$bin" get "${vip}" "${port_secure}")" + echo " secured service reports: ${got}" + echo "${got}" | grep -q "secure_tcp=3D1" || + { echo -e "${RED}FAIL${NC}: flag not set"; ret=3D1; } + got=3D"$(ip netns exec "${ns1}" "$bin" get "${vip}" "${port_plain}")" + echo "${got}" | grep -q "secure_tcp=3D0" || + { echo -e "${RED}FAIL${NC}: flag unexpectedly set"; ret=3D1; } + + # The probes carry TTL=3D1, so IPVS updates the connection state and + # then the packet expires before reaching the real server (which + # stays silent, no RST to interfere with the observation). + + # Push SYN then ACK to each service from the client + ip netns exec "${ns0}" "$probe" "${cip}" 40000 "${vip}" "${port_secure}" + ip netns exec "${ns0}" "$probe" "${cip}" 40001 "${vip}" "${port_plain}" + sleep 1 + + echo "Testing per-service secure_tcp..." + echo " --- connection table (ipvsadm -Lnc) ---" + ip netns exec "${ns1}" ipvsadm -Lnc 2>/dev/null + echo " --- end connection table ---" + assert_state "${port_plain}" ESTABLISHED + assert_state "${port_secure}" SYN_RECV +} + +trap cleanup EXIT + +setup +test_secure + +if [ "$ret" -ne 0 ]; then + echo -e "$(basename $0): ${RED}FAIL${NC}" + exit 1 +fi +echo -e "$(basename $0): ${GREEN}PASS${NC}" +exit 0 diff --git a/tools/testing/selftests/net/netfilter/ipvs_secure_tcp_mln.c b/= tools/testing/selftests/net/netfilter/ipvs_secure_tcp_mln.c new file mode 100644 index 000000000000..54c6d860addf --- /dev/null +++ b/tools/testing/selftests/net/netfilter/ipvs_secure_tcp_mln.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * libmnl helper to set/query the per-service secure_tcp flag + * (IP_VS_SVC_F_SECURE_TCP), which ipvsadm does not expose. + * + * Usage: + * ipvs_secure_tcp_mln add + * Create a TCP virtual service (scheduler "rr") with the flag either + * set or not. Add real servers afterwards with: + * ipvsadm -a -t : -r : + * ipvs_secure_tcp_mln get + * Print "secure_tcp=3D<0|1>" for the service. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Fallback in case the kernel's installed uapi header is older */ +#ifndef IP_VS_SVC_F_SECURE_TCP +#define IP_VS_SVC_F_SECURE_TCP 0x0100 +#endif + +/* 16-byte address storage, matching union nf_inet_addr for AF_INET */ +struct inet_addr16 { + uint8_t all[16]; +}; + +/* ---------------- family resolver ---------------- */ +static int ctrl_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb =3D data; + int type =3D mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + if (type =3D=3D CTRL_ATTR_FAMILY_ID) { + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + tb[CTRL_ATTR_FAMILY_ID] =3D attr; + } + return MNL_CB_OK; +} + +static int ctrl_data_cb(const struct nlmsghdr *nlh, void *data) +{ + const struct nlattr *tb[CTRL_ATTR_MAX + 1] =3D { 0 }; + uint16_t *fam =3D data; + + if (nlh->nlmsg_type !=3D GENL_ID_CTRL) + return MNL_CB_OK; + mnl_attr_parse(nlh, sizeof(struct genlmsghdr), + (mnl_attr_cb_t)ctrl_attr_cb, tb); + if (tb[CTRL_ATTR_FAMILY_ID]) { + *fam =3D mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + return MNL_CB_STOP; + } + return MNL_CB_OK; +} + +static int resolve_family(const char *name, uint16_t *fam) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + int ret; + + *fam =3D 0; + nl =3D mnl_socket_open(NETLINK_GENERIC); + if (!nl) + return -errno; + if (mnl_socket_bind(nl, 0, 0) < 0) { + mnl_socket_close(nl); + return -errno; + } + + nlh =3D mnl_nlmsg_put_header(buf); + genl =3D mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd =3D CTRL_CMD_GETFAMILY; + genl->version =3D 1; + nlh->nlmsg_type =3D GENL_ID_CTRL; + nlh->nlmsg_flags =3D NLM_F_REQUEST; + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, name); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + mnl_socket_close(nl); + return -errno; + } + do { + ret =3D mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) { + if (errno =3D=3D EAGAIN) + continue; + mnl_socket_close(nl); + return -errno; + } + ret =3D mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), + (mnl_cb_t)ctrl_data_cb, fam); + } while (ret > 0 && *fam =3D=3D 0); + + mnl_socket_close(nl); + return *fam ? 0 : -ENOENT; +} + +/* ---------------- fill service identifying attrs ---------------- */ +static int fill_service(struct nlmsghdr *nlh, const char *vip, + uint16_t port, int full, int secure) +{ + struct inet_addr16 vaddr =3D { 0 }; + struct nlattr *nest; + struct ip_vs_flags fl; + int af =3D AF_INET; + + if (inet_pton(af, vip, vaddr.all) !=3D 1) { + fprintf(stderr, "bad VIP %s\n", vip); + return -EINVAL; + } + + nest =3D mnl_attr_nest_start(nlh, IPVS_CMD_ATTR_SERVICE); + mnl_attr_put_u16(nlh, IPVS_SVC_ATTR_AF, af); + mnl_attr_put_u16(nlh, IPVS_SVC_ATTR_PROTOCOL, IPPROTO_TCP); + mnl_attr_put(nlh, IPVS_SVC_ATTR_ADDR, sizeof(vaddr), &vaddr); + /* port/be16: port is passed in network order from main() */ + mnl_attr_put_u16(nlh, IPVS_SVC_ATTR_PORT, port); + + if (full) { + mnl_attr_put_strz(nlh, IPVS_SVC_ATTR_SCHED_NAME, "rr"); + memset(&fl, 0, sizeof(fl)); + fl.mask =3D IP_VS_SVC_F_SECURE_TCP; + if (secure) + fl.flags =3D IP_VS_SVC_F_SECURE_TCP; + mnl_attr_put(nlh, IPVS_SVC_ATTR_FLAGS, sizeof(fl), &fl); + mnl_attr_put_u32(nlh, IPVS_SVC_ATTR_TIMEOUT, 0); + mnl_attr_put_u32(nlh, IPVS_SVC_ATTR_NETMASK, 0xffffffff); + } + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int send_cmd(struct mnl_socket *nl, struct nlmsghdr *nlh) +{ + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("sendto"); + return -1; + } + return 0; +} + +/* ---------------- get secure flag ---------------- */ +static int svc_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb =3D data; + int type =3D mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, IPVS_SVC_ATTR_MAX) < 0) + return MNL_CB_ERROR; + tb[type] =3D attr; + return MNL_CB_OK; +} + +static int get_cb(const struct nlmsghdr *nlh, void *data) +{ + const struct nlattr *tb[IPVS_SVC_ATTR_MAX + 1] =3D { 0 }; + struct ip_vs_flags fl; + int *secure =3D data; + struct nlattr *nest; + + mnl_attr_for_each(nest, nlh, sizeof(struct genlmsghdr)) { + if (mnl_attr_get_type(nest) =3D=3D IPVS_CMD_ATTR_SERVICE) + mnl_attr_parse_nested(nest, (mnl_attr_cb_t)svc_attr_cb, tb); + } + if (tb[IPVS_SVC_ATTR_FLAGS] && + mnl_attr_get_payload_len(tb[IPVS_SVC_ATTR_FLAGS]) >=3D sizeof(fl)) { + memcpy(&fl, mnl_attr_get_payload(tb[IPVS_SVC_ATTR_FLAGS]), + sizeof(fl)); + *secure =3D !!(fl.flags & IP_VS_SVC_F_SECURE_TCP); + } + return MNL_CB_STOP; +} + +static int do_get(uint16_t fam, const char *vip, uint16_t port) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + int ret, secure =3D -1; + + nl =3D mnl_socket_open(NETLINK_GENERIC); + if (!nl) + return -errno; + if (mnl_socket_bind(nl, 0, 0) < 0) { + mnl_socket_close(nl); + return -errno; + } + nlh =3D mnl_nlmsg_put_header(buf); + genl =3D mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd =3D IPVS_CMD_GET_SERVICE; + genl->version =3D IPVS_GENL_VERSION; + nlh->nlmsg_type =3D fam; + nlh->nlmsg_flags =3D NLM_F_REQUEST; + ret =3D fill_service(nlh, vip, port, 0, 0); + if (ret < 0) { + mnl_socket_close(nl); + return ret; + } + if (send_cmd(nl, nlh) < 0) { + mnl_socket_close(nl); + return -1; + } + + ret =3D mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret >=3D 0) { + ret =3D mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), + (mnl_cb_t)get_cb, &secure); + if (ret <=3D MNL_CB_STOP || secure >=3D 0) + break; + ret =3D mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + mnl_socket_close(nl); + if (secure < 0) + return -ENOENT; + printf("secure_tcp=3D%d\n", secure); + return 0; +} + +/* ---------------- add service with flag ---------------- */ +static int do_add(uint16_t fam, const char *vip, uint16_t port, int secure) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + int ret; + + /* NLM_F_EXCL: fail if the service already exists */ + nlh =3D mnl_nlmsg_put_header(buf); + genl =3D mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd =3D IPVS_CMD_NEW_SERVICE; + genl->version =3D IPVS_GENL_VERSION; + nlh->nlmsg_type =3D fam; + nlh->nlmsg_flags =3D NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXC= L; + ret =3D fill_service(nlh, vip, port, 1, secure); + if (ret < 0) + return 1; + + nl =3D mnl_socket_open(NETLINK_GENERIC); + if (!nl) + return 1; + if (mnl_socket_bind(nl, 0, 0) < 0) { + mnl_socket_close(nl); + return 1; + } + if (send_cmd(nl, nlh) < 0) { + mnl_socket_close(nl); + return 1; + } + + /* Read the reply so we can report why a command may have failed */ + for (;;) { + ret =3D mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret <=3D 0) { + fprintf(stderr, "no reply from IPVS\n"); + mnl_socket_close(nl); + return 1; + } + ret =3D mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), + NULL, NULL); + if (ret < 0) { + int e =3D errno; + + fprintf(stderr, "IPVS netlink error: ret=3D%d errno=3D%d (%s)\n", + ret, e, strerror(e)); + mnl_socket_close(nl); + return 1; + } + if (ret <=3D MNL_CB_STOP) + break; + } + mnl_socket_close(nl); + return 0; +} + +int main(int argc, char *argv[]) +{ + const char *cmd, *vip; + uint16_t fam =3D 0; + uint16_t port; + int ret, secure =3D 0; + + if (argc < 4) { + fprintf(stderr, + "usage: %s add \n" + " %s get \n", argv[0], argv[0]); + return 2; + } + cmd =3D argv[1]; + vip =3D argv[2]; + port =3D (uint16_t)atoi(argv[3]); + port =3D htons(port); + + ret =3D resolve_family(IPVS_GENL_NAME, &fam); + if (ret) { + fprintf(stderr, "cannot resolve IPVS genl family: %s\n", + strerror(-ret)); + return 1; + } + + if (strcmp(cmd, "add") =3D=3D 0) { + if (argc < 5) { + fprintf(stderr, "usage: %s add ... \n", + argv[0]); + return 2; + } + if (strcmp(argv[4], "secure") =3D=3D 0) { + secure =3D 1; + } else if (strcmp(argv[4], "plain") !=3D 0) { + fprintf(stderr, "unknown mode %s\n", argv[4]); + return 2; + } + return do_add(fam, vip, port, secure); + } else if (strcmp(cmd, "get") =3D=3D 0) { + return do_get(fam, vip, port); + } + + fprintf(stderr, "unknown command %s\n", cmd); + return 2; +} --=20 2.51.0