[PATCH mptcp-next 2/2] mptcp: add MIB counter for received invalid options

Gang Yan <[email protected]>
Newsgroups dev.linux.lists.mptcp
Message-ID <[email protected]>
From: Gang Yan <[email protected]>

In mptcp_parse_option(), an MPTCP option is silently ignored via a
'break' under a number of conditions.

This patch adds a MIB counter tracking this as an evidence to show
someone is trying to attack the peer

Assisted-by: Claude:GLM-5.2
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/628
Signed-off-by: Gang Yan <[email protected]>
---
 net/mptcp/mib.c     |  1 +
 net/mptcp/mib.h     |  1 +
 net/mptcp/options.c | 98 +++++++++++++++++++++++++++++++++------------
 3 files changed, 74 insertions(+), 26 deletions(-)

diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c
index 608cb568897c..10e8870dd985 100644
--- a/net/mptcp/mib.c
+++ b/net/mptcp/mib.c
@@ -95,6 +95,7 @@ static const struct snmp_mib mptcp_snmp_list[] = {
 	SNMP_MIB_ITEM("BacklogDrop", MPTCP_MIB_BACKLOGDROP),
 	SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED),
 	SNMP_MIB_ITEM("OFOPruned", MPTCP_MIB_OFOPRUNED),
+	SNMP_MIB_ITEM("InvalidOptionRx", MPTCP_MIB_INVALDOPTIONRX),
 };
 
 /* mptcp_mib_alloc - allocate percpu mib counters
diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h
index 1ebdb55e9534..ae0751addd64 100644
--- a/net/mptcp/mib.h
+++ b/net/mptcp/mib.h
@@ -98,6 +98,7 @@ enum linux_mptcp_mib_field {
 	MPTCP_MIB_BACKLOGDROP,		/* Backlog over memory limit */
 	MPTCP_MIB_RCVPRUNED,		/* Dropped due to memory constraints */
 	MPTCP_MIB_OFOPRUNED,		/* MPTCP-level OoO queue pruned */
+	MPTCP_MIB_INVALDOPTIONRX,	/* Received an invalid MPTCP option */
 	__MPTCP_MIB_MAX
 };
 
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index dfdcc888efe3..e9deea0e483a 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -53,9 +53,12 @@ static void mptcp_parse_option(struct net *net,
 
 		/* Only the MPC + ACK can be used with a RM_ADDR */
 		if (subopt == OPTION_MPTCP_MPC_ACK) {
-			if ((mp_opt->suboptions & ~OPTION_MPTCP_RM_ADDR) != 0)
+			if ((mp_opt->suboptions & ~OPTION_MPTCP_RM_ADDR) != 0) {
+				MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 				break;
+			}
 		} else if (mp_opt->suboptions != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
 		}
 
@@ -71,22 +74,29 @@ static void mptcp_parse_option(struct net *net,
 		 */
 		if (opsize != expected_opsize &&
 		    (expected_opsize != TCPOLEN_MPTCP_MPC_ACK_DATA ||
-		     opsize != TCPOLEN_MPTCP_MPC_ACK_DATA_CSUM))
+		     opsize != TCPOLEN_MPTCP_MPC_ACK_DATA_CSUM)) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		/* try to be gentle vs future versions on the initial syn */
 		version = *ptr++ & MPTCP_VERSION_MASK;
 		if (opsize != TCPOLEN_MPTCP_MPC_SYN) {
-			if (version != MPTCP_SUPPORTED_VERSION)
+			if (version != MPTCP_SUPPORTED_VERSION) {
+				MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 				break;
+			}
 		} else if (version < MPTCP_SUPPORTED_VERSION) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
 		}
 
 		flags = *ptr++;
 		if (!mptcp_cap_flag_sha256(flags) ||
-		    (flags & MPTCP_CAP_EXTENSIBILITY))
+		    (flags & MPTCP_CAP_EXTENSIBILITY)) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		/* RFC 6824, Section 3.1:
 		 * "For the Checksum Required bit (labeled "A"), if either
@@ -133,8 +143,10 @@ static void mptcp_parse_option(struct net *net,
 	case MPTCPOPT_MP_JOIN:
 		/* Can be used with a restricted number of other options */
 		if ((mp_opt->suboptions & ~(OPTION_MPTCP_RM_ADDR |
-					    OPTION_MPTCP_PRIO)) != 0)
+					    OPTION_MPTCP_PRIO)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		if (opsize == TCPOLEN_MPTCP_MPJ_SYN) {
 			mp_opt->suboptions |= OPTION_MPTCP_MPJ_SYN;
@@ -163,6 +175,8 @@ static void mptcp_parse_option(struct net *net,
 			ptr += 2;
 			memcpy(mp_opt->hmac, ptr, MPTCPOPT_HMAC_LEN);
 			pr_debug("MP_JOIN hmac\n");
+		} else {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 		}
 		break;
 
@@ -172,8 +186,10 @@ static void mptcp_parse_option(struct net *net,
 					    OPTION_MPTCP_RM_ADDR |
 					    OPTION_MPTCP_PRIO |
 					    OPTION_MPTCP_FASTCLOSE |
-					    OPTION_MPTCP_FAIL)) != 0)
+					    OPTION_MPTCP_FAIL)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		pr_debug("DSS\n");
 		ptr++;
@@ -216,6 +232,7 @@ static void mptcp_parse_option(struct net *net,
 			mp_opt->ack64 = 0;
 			mp_opt->use_ack = 0;
 			mp_opt->data_fin = 0;
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
 		}
 
@@ -265,32 +282,38 @@ static void mptcp_parse_option(struct net *net,
 		/* Can be used with a restricted number of other options */
 		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS |
 					    OPTION_MPTCP_RM_ADDR |
-					    OPTION_MPTCP_PRIO)) != 0)
+					    OPTION_MPTCP_PRIO)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		mp_opt->echo = (*ptr++) & MPTCP_ADDR_ECHO;
 		if (!mp_opt->echo) {
 			if (opsize == TCPOLEN_MPTCP_ADD_ADDR ||
-			    opsize == TCPOLEN_MPTCP_ADD_ADDR_PORT)
+			    opsize == TCPOLEN_MPTCP_ADD_ADDR_PORT) {
 				mp_opt->addr.family = AF_INET;
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-			else if (opsize == TCPOLEN_MPTCP_ADD_ADDR6 ||
-				 opsize == TCPOLEN_MPTCP_ADD_ADDR6_PORT)
+			} else if (opsize == TCPOLEN_MPTCP_ADD_ADDR6 ||
+				 opsize == TCPOLEN_MPTCP_ADD_ADDR6_PORT) {
 				mp_opt->addr.family = AF_INET6;
 #endif
-			else
+			} else {
+				MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 				break;
+			}
 		} else {
 			if (opsize == TCPOLEN_MPTCP_ADD_ADDR_BASE ||
-			    opsize == TCPOLEN_MPTCP_ADD_ADDR_BASE_PORT)
+			    opsize == TCPOLEN_MPTCP_ADD_ADDR_BASE_PORT) {
 				mp_opt->addr.family = AF_INET;
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-			else if (opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE ||
-				 opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT)
+			} else if (opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE ||
+				 opsize == TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT) {
 				mp_opt->addr.family = AF_INET6;
 #endif
-			else
+			} else {
+				MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 				break;
+			}
 		}
 
 		mp_opt->suboptions |= OPTION_MPTCP_ADD_ADDR;
@@ -332,12 +355,16 @@ static void mptcp_parse_option(struct net *net,
 					    OPTIONS_MPTCP_MPJ |
 					    OPTIONS_MPTCP_DSS |
 					    OPTION_MPTCP_ADD_ADDR |
-					    OPTION_MPTCP_PRIO)) != 0)
+					    OPTION_MPTCP_PRIO)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		if (opsize < TCPOLEN_MPTCP_RM_ADDR_BASE + 1 ||
-		    opsize > TCPOLEN_MPTCP_RM_ADDR_BASE + MPTCP_RM_IDS_MAX)
+		    opsize > TCPOLEN_MPTCP_RM_ADDR_BASE + MPTCP_RM_IDS_MAX) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		ptr++;
 
@@ -353,11 +380,15 @@ static void mptcp_parse_option(struct net *net,
 		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_MPJ |
 					    OPTIONS_MPTCP_DSS |
 					    OPTION_MPTCP_ADD_ADDR |
-					    OPTION_MPTCP_RM_ADDR)) != 0)
+					    OPTION_MPTCP_RM_ADDR)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
-		if (opsize != TCPOLEN_MPTCP_PRIO)
+		if (opsize != TCPOLEN_MPTCP_PRIO) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		mp_opt->suboptions |= OPTION_MPTCP_PRIO;
 		mp_opt->backup = *ptr++ & MPTCP_PRIO_BKUP;
@@ -367,11 +398,15 @@ static void mptcp_parse_option(struct net *net,
 	case MPTCPOPT_MP_FASTCLOSE:
 		/* Can be used with a restricted number of other options */
 		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS |
-					    OPTION_MPTCP_RST)) != 0)
+					    OPTION_MPTCP_RST)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
-		if (opsize != TCPOLEN_MPTCP_FASTCLOSE)
+		if (opsize != TCPOLEN_MPTCP_FASTCLOSE) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		ptr += 2;
 		mp_opt->rcvr_key = get_unaligned_be64(ptr);
@@ -383,14 +418,20 @@ static void mptcp_parse_option(struct net *net,
 	case MPTCPOPT_RST:
 		/* Can be used with a restricted number of other options */
 		if ((mp_opt->suboptions & ~(OPTION_MPTCP_FAIL |
-					    OPTION_MPTCP_FASTCLOSE)) != 0)
+					    OPTION_MPTCP_FASTCLOSE)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
-		if (opsize != TCPOLEN_MPTCP_RST)
+		if (opsize != TCPOLEN_MPTCP_RST) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
-		if (!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_RST))
+		if (!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_RST)) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		mp_opt->suboptions |= OPTION_MPTCP_RST;
 		flags = *ptr++;
@@ -403,11 +444,15 @@ static void mptcp_parse_option(struct net *net,
 	case MPTCPOPT_MP_FAIL:
 		/* Can be used with a restricted number of other options */
 		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS |
-					    OPTION_MPTCP_RST)) != 0)
+					    OPTION_MPTCP_RST)) != 0) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
-		if (opsize != TCPOLEN_MPTCP_FAIL)
+		if (opsize != TCPOLEN_MPTCP_FAIL) {
+			MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 			break;
+		}
 
 		ptr += 2;
 		mp_opt->suboptions |= OPTION_MPTCP_FAIL;
@@ -416,6 +461,7 @@ static void mptcp_parse_option(struct net *net,
 		break;
 
 	default:
+		MPTCP_INC_STATS(net, MPTCP_MIB_INVALDOPTIONRX);
 		break;
 	}
 }
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.