[qca-nss-sfe] 802.1Q/802.1ad VLAN support in SFE
Change-Id: I8007484b229b0dac0aff6dc284641b94090539db
Signed-off-by: Wayne Tan <quic_wtan@quicinc.com>
diff --git a/sfe.c b/sfe.c
index b8c8040..1cb88a2 100644
--- a/sfe.c
+++ b/sfe.c
@@ -31,6 +31,7 @@
#include "sfe_api.h"
#include "sfe.h"
#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
extern int max_ipv4_conn;
extern int max_ipv6_conn;
@@ -457,36 +458,51 @@
sfe_l2_hdr_offset_set(l2_info, ((skb->data - ETH_HLEN) - skb->head));
/*
- * TODO: Add VLAN parsing here.
- * Add VLAN fields to l2_info structure and update l2_hdr_size
- * In case of exception, use l2_hdr_size to move the data pointer back
+ * VLAN parsing
*/
+ if (unlikely(!sfe_vlan_check_and_parse_tag(skb, l2_info))) {
+ return false;
+ }
/*
* PPPoE parsing
*/
- if (unlikely(htons(ETH_P_PPP_SES) != skb->protocol)) {
- return false;
- }
+ if (htons(ETH_P_PPP_SES) == skb->protocol) {
+ /*
+ * Parse only PPPoE session packets
+ * skb->data is pointing to PPPoE hdr
+ */
+ if (!sfe_pppoe_parse_hdr(skb, l2_info)) {
- /*
- * Parse only PPPoE session packets
- * skb->data is pointing to PPPoE hdr
- */
- if (!sfe_pppoe_parse_hdr(skb, l2_info)) {
+ /*
+ * For exception from PPPoE return from here without modifying the skb->data
+ * This includes non-IPv4/v6 cases also
+ */
+ return false;
+ }
/*
- * For exception from PPPoE return from here without modifying the skb->data
- * This includes non-IPv4/v6 cases also
+ * Pull by L2 header size
*/
- return false;
+ __skb_pull(skb, sfe_l2_hdr_size_get(l2_info));
}
+ return true;
+}
+
+/*
+ * sfe_recv_undo_parse_l2()
+ */
+static void sfe_recv_undo_parse_l2(struct net_device *dev, struct sk_buff *skb, struct sfe_l2_info *l2_info)
+{
+ /*
+ * PPPoE undo
+ */
+ __skb_push(skb, sfe_l2_hdr_size_get(l2_info));
/*
- * Pull by L2 header size considering all L2.5 headers
+ * VLAN undo
*/
- __skb_pull(skb, sfe_l2_hdr_size_get(l2_info));
- return true;
+ sfe_vlan_undo_parse(skb, l2_info);
}
/*
@@ -1143,6 +1159,8 @@
* Setting parse flags to 0 since l2_info is passed for non L2.5 header case as well
*/
l2_info.parse_flags = 0;
+ l2_info.l2_hdr_size = 0;
+ l2_info.vlan_hdr_cnt = 0;
#ifdef CONFIG_NET_CLS_ACT
/*
@@ -1170,7 +1188,7 @@
return sfe_ipv4_recv(dev, skb, &l2_info, false);
}
- DEBUG_TRACE("No IPv4 address for device: %s\n", dev->name);
+ DEBUG_TRACE("No IPv4 address for device: %s skb=%px\n", dev->name, skb);
return 0;
case ETH_P_IPV6:
@@ -1178,7 +1196,7 @@
return sfe_ipv6_recv(dev, skb, &l2_info, false);
}
- DEBUG_TRACE("No IPv6 address for device: %s\n", dev->name);
+ DEBUG_TRACE("No IPv6 address for device: %s skb=%px\n", dev->name, skb);
return 0;
default:
@@ -1189,7 +1207,8 @@
* Stop L2 processing if L2 feature is disabled.
*/
if (!sfe_is_l2_feature_enabled()) {
- DEBUG_TRACE("Unsupported protocol %d (L2 feature is disabled)\n", ntohs(skb->protocol));
+ DEBUG_TRACE("Unsupported protocol %#x %s (L2 feature is disabled) skb=%px\n",
+ ntohs(skb->protocol), dev->name, skb);
return 0;
}
@@ -1197,8 +1216,8 @@
* Parse the L2 headers to find the L3 protocol and the L2 header offset
*/
if (unlikely(!sfe_recv_parse_l2(dev, skb, &l2_info))) {
- DEBUG_TRACE("%px: Invalid L2.5 header format with protocol : %d\n", skb, ntohs(skb->protocol));
- return 0;
+ DEBUG_TRACE("%px: Invalid L2.5 header format with protocol : %x\n", skb, ntohs(skb->protocol));
+ goto send_to_linux;
}
/*
@@ -1215,18 +1234,24 @@
if (likely(l2_info.protocol == ETH_P_IPV6)) {
ret = sfe_ipv6_recv(dev, skb, &l2_info, false);
- if (likely(ret)) {
- return ret;
+ if (unlikely(!ret)) {
+ goto send_to_linux;
}
+ return ret;
}
+ DEBUG_TRACE("Non-IP(%x) %s skb=%px skb_vlan:%x/%x/%x skb_proto=%x\n",
+ l2_info.protocol, dev->name, skb,
+ ntohs(skb->vlan_proto), skb->vlan_tci, skb_vlan_tag_present(skb),
+ htons(skb->protocol));
+
send_to_linux:
/*
* Push the data back before sending to linux if -
* a. There is any exception from IPV4/V6
* b. If the next protocol is neither IPV4 nor IPV6
*/
- __skb_push(skb, sfe_l2_hdr_size_get(&l2_info));
+ sfe_recv_undo_parse_l2(dev, skb, &l2_info);
return 0;
}
diff --git a/sfe.h b/sfe.h
index 87dba90..f4b96b6 100644
--- a/sfe.h
+++ b/sfe.h
@@ -53,6 +53,14 @@
} sfe_sync_reason_t;
/*
+ * VLAN header (aka VLAN tag)
+ */
+struct sfe_vlan_hdr {
+ u16 tpid; /* Tag Protocol Identifier */
+ u16 tci; /* Tag Control Information */
+};
+
+/*
* Structure used to store L2 information
*/
struct sfe_l2_info {
@@ -61,6 +69,9 @@
u16 l2_hdr_size; /* L2 header size */
u16 pppoe_hdr_offset; /* PPPOE header offset */
u16 protocol; /* L3 Protocol */
+ struct sfe_vlan_hdr vlan_hdr[SFE_MAX_VLAN_DEPTH];
+ /* VLAN tag(s) of ingress packet */
+ u8 vlan_hdr_cnt; /* Number of VLAN tags in the ingress packet */
};
/*
@@ -96,7 +107,7 @@
u64 dest_byte_count;
u32 dest_new_packet_count;
u32 dest_new_byte_count;
- u32 reason; /* reason for stats sync message, i.e. destroy, flush, period sync */
+ u32 reason; /* reason for stats sync message, i.e. destroy, flush, period sync */
u64 delta_jiffies; /* Time to be added to the current timeout to keep the connection alive */
};
diff --git a/sfe_ipv4.c b/sfe_ipv4.c
index a93ed7e..d4cd870 100644
--- a/sfe_ipv4.c
+++ b/sfe_ipv4.c
@@ -41,6 +41,7 @@
#include "sfe_ipv4_udp.h"
#include "sfe_ipv4_tcp.h"
#include "sfe_ipv4_icmp.h"
+#include "sfe_pppoe.h"
static char *sfe_ipv4_exception_events_string[SFE_IPV4_EXCEPTION_EVENT_LAST] = {
"UDP_HEADER_INCOMPLETE",
@@ -906,6 +907,59 @@
}
}
+/*
+ * sfe_ipv4_match_entry_set_vlan()
+ */
+static void sfe_ipv4_match_entry_set_vlan(
+ struct sfe_ipv4_connection_match *cm,
+ u32 primary_ingress_vlan_tag,
+ u32 primary_egress_vlan_tag,
+ u32 secondary_ingress_vlan_tag,
+ u32 secondary_egress_vlan_tag)
+{
+ u16 tpid;
+ /*
+ * Prevent stacking header counts when updating.
+ */
+ cm->ingress_vlan_hdr_cnt = 0;
+ cm->egress_vlan_hdr_cnt = 0;
+ memset(cm->ingress_vlan_hdr, 0, sizeof(cm->ingress_vlan_hdr));
+ memset(cm->egress_vlan_hdr, 0, sizeof(cm->egress_vlan_hdr));
+
+ /*
+ * vlan_hdr[0] corresponds to outer tag
+ * vlan_hdr[1] corresponds to inner tag
+ * Extract the vlan information (tpid and tci) from rule message
+ */
+ if ((primary_ingress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(primary_ingress_vlan_tag >> 16);
+ cm->ingress_vlan_hdr[0].tpid = ntohs(tpid);
+ cm->ingress_vlan_hdr[0].tci = (u16)primary_ingress_vlan_tag;
+ cm->ingress_vlan_hdr_cnt++;
+ }
+
+ if ((secondary_ingress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(secondary_ingress_vlan_tag >> 16);
+ cm->ingress_vlan_hdr[1].tpid = ntohs(tpid);
+ cm->ingress_vlan_hdr[1].tci = (u16)secondary_ingress_vlan_tag;
+ cm->ingress_vlan_hdr_cnt++;
+ }
+
+ if ((primary_egress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(primary_egress_vlan_tag >> 16);
+ cm->egress_vlan_hdr[0].tpid = ntohs(tpid);
+ cm->egress_vlan_hdr[0].tci = (u16)primary_egress_vlan_tag;
+ cm->egress_vlan_hdr_cnt++;
+ }
+
+ if ((secondary_egress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(secondary_egress_vlan_tag >> 16);
+ cm->egress_vlan_hdr[1].tpid = ntohs(tpid);
+ cm->egress_vlan_hdr[1].tci = (u16)secondary_egress_vlan_tag;
+ cm->egress_vlan_hdr_cnt++;
+ }
+}
+
void sfe_ipv4_update_rule(struct sfe_ipv4_rule_create_msg *msg)
{
struct sfe_ipv4_connection *c;
@@ -1042,11 +1096,11 @@
* trying to create. If there is then we can't create a new one.
*/
c_old = sfe_ipv4_find_connection(si,
- msg->tuple.protocol,
- msg->tuple.flow_ip,
- msg->tuple.flow_ident,
- msg->tuple.return_ip,
- msg->tuple.return_ident);
+ msg->tuple.protocol,
+ msg->tuple.flow_ip,
+ msg->tuple.flow_ident,
+ msg->tuple.return_ip,
+ msg->tuple.return_ident);
if (c_old != NULL) {
this_cpu_inc(si->stats_pcpu->connection_create_collisions64);
@@ -1092,6 +1146,7 @@
original_cm->xlate_src_port = msg->conn_rule.flow_ident_xlate;
original_cm->xlate_dest_ip = msg->conn_rule.return_ip_xlate;
original_cm->xlate_dest_port =msg->conn_rule.return_ident_xlate;
+
atomic_set(&original_cm->rx_packet_count, 0);
original_cm->rx_packet_count64 = 0;
atomic_set(&original_cm->rx_byte_count, 0);
@@ -1102,13 +1157,14 @@
original_cm->connection = c;
original_cm->counter_match = reply_cm;
+ original_cm->l2_hdr_size = 0;
+ original_cm->flags = 0;
/*
* UDP Socket is valid only in decap direction.
*/
RCU_INIT_POINTER(original_cm->up, NULL);
- original_cm->flags = 0;
if (msg->valid_flags & SFE_RULE_CREATE_MARK_VALID) {
original_cm->mark = msg->mark_rule.flow_mark;
original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_MARK;
@@ -1126,6 +1182,25 @@
original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_BRIDGE_FLOW;
}
+ /*
+ * Add VLAN rule to original_cm
+ */
+ if (msg->valid_flags & SFE_RULE_CREATE_VLAN_VALID) {
+ struct sfe_vlan_rule *vlan_primary_rule = &msg->vlan_primary_rule;
+ struct sfe_vlan_rule *vlan_secondary_rule = &msg->vlan_secondary_rule;
+ sfe_ipv4_match_entry_set_vlan(original_cm,
+ vlan_primary_rule->ingress_vlan_tag,
+ vlan_primary_rule->egress_vlan_tag,
+ vlan_secondary_rule->ingress_vlan_tag,
+ vlan_secondary_rule->egress_vlan_tag);
+
+ if ((msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE) &&
+ original_cm->egress_vlan_hdr_cnt > 0) {
+ original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG;
+ original_cm->l2_hdr_size += original_cm->egress_vlan_hdr_cnt * VLAN_HLEN;
+ }
+ }
+
#ifdef CONFIG_NF_FLOW_COOKIE
original_cm->flow_cookie = 0;
#endif
@@ -1149,6 +1224,7 @@
}
}
+ reply_cm->l2_hdr_size = 0;
reply_cm->flags = 0;
/*
@@ -1169,12 +1245,14 @@
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
+ reply_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
reply_cm->pppoe_session_id = msg->pppoe_rule.flow_pppoe_session_id;
ether_addr_copy(reply_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
}
if (msg->valid_flags & SFE_RULE_CREATE_PPPOE_ENCAP_VALID) {
original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
+ original_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
original_cm->pppoe_session_id = msg->pppoe_rule.return_pppoe_session_id;
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.return_pppoe_remote_mac);
@@ -1207,6 +1285,7 @@
ether_addr_copy((u8 *)original_cm->xmit_dest_mac, (u8 *)msg->conn_rule.return_mac);
original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR;
+ original_cm->l2_hdr_size += ETH_HLEN;
/*
* If our dev writes Ethernet headers then we can write a really fast
@@ -1263,6 +1342,7 @@
reply_cm->priority = msg->qos_rule.return_qos_tag;
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_PRIORITY_REMARK;
}
+
if (msg->valid_flags & SFE_RULE_CREATE_DSCP_MARKING_VALID) {
reply_cm->dscp = msg->dscp_rule.return_dscp << SFE_IPV4_DSCP_SHIFT;
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK;
@@ -1305,6 +1385,7 @@
#else
if (!refcount_inc_not_zero(&sk->sk_refcnt)) {
#endif
+ spin_unlock_bh(&si->lock);
kfree(reply_cm);
kfree(original_cm);
kfree(c);
@@ -1333,6 +1414,25 @@
ntohs(reply_cm->match_dest_port), ntohs(reply_cm->xlate_dest_port));
}
+ /*
+ * Add VLAN rule to reply_cm
+ */
+ if (msg->valid_flags & SFE_RULE_CREATE_VLAN_VALID) {
+ struct sfe_vlan_rule *vlan_primary_rule = &msg->vlan_primary_rule;
+ struct sfe_vlan_rule *vlan_secondary_rule = &msg->vlan_secondary_rule;
+ sfe_ipv4_match_entry_set_vlan(reply_cm,
+ vlan_primary_rule->egress_vlan_tag,
+ vlan_primary_rule->ingress_vlan_tag,
+ vlan_secondary_rule->egress_vlan_tag,
+ vlan_secondary_rule->ingress_vlan_tag);
+
+ if ((msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE) &&
+ reply_cm->egress_vlan_hdr_cnt > 0) {
+ reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG;
+ reply_cm->l2_hdr_size += reply_cm->egress_vlan_hdr_cnt * VLAN_HLEN;
+ }
+ }
+
#ifdef CONFIG_NF_FLOW_COOKIE
reply_cm->flow_cookie = 0;
#endif
@@ -1381,6 +1481,7 @@
ether_addr_copy((u8 *)reply_cm->xmit_dest_mac, (u8 *)msg->conn_rule.flow_mac);
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR;
+ reply_cm->l2_hdr_size += ETH_HLEN;
/*
* If our dev writes Ethernet headers then we can write a really fast
@@ -1405,23 +1506,6 @@
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_XLATE_DEST;
}
- c->protocol = tuple->protocol;
- c->src_ip = tuple->flow_ip;
- c->src_ip_xlate = msg->conn_rule.flow_ip_xlate;
- c->src_port = tuple->flow_ident;
- c->src_port_xlate = msg->conn_rule.flow_ident_xlate;
- c->original_dev = src_dev;
- c->original_match = original_cm;
- c->dest_ip = tuple->return_ip;
- c->dest_ip_xlate = msg->conn_rule.return_ip_xlate;
- c->dest_port = tuple->return_ident;
- c->dest_port_xlate = msg->conn_rule.return_ident_xlate;
- c->reply_dev = dest_dev;
- c->reply_match = reply_cm;
- c->debug_read_seq = 0;
- c->last_sync_jiffies = get_jiffies_64();
- c->removed = false;
-
/*
* Initialize the protocol-specific information that we track.
*/
@@ -1444,6 +1528,26 @@
break;
}
+ /*
+ * Fill in the ipv4_connection object.
+ */
+ c->protocol = tuple->protocol;
+ c->src_ip = tuple->flow_ip;
+ c->src_ip_xlate = msg->conn_rule.flow_ip_xlate;
+ c->src_port = tuple->flow_ident;
+ c->src_port_xlate = msg->conn_rule.flow_ident_xlate;
+ c->original_dev = src_dev;
+ c->original_match = original_cm;
+ c->dest_ip = tuple->return_ip;
+ c->dest_ip_xlate = msg->conn_rule.return_ip_xlate;
+ c->dest_port = tuple->return_ident;
+ c->dest_port_xlate = msg->conn_rule.return_ident_xlate;
+ c->reply_dev = dest_dev;
+ c->reply_match = reply_cm;
+ c->debug_read_seq = 0;
+ c->last_sync_jiffies = get_jiffies_64();
+ c->removed = false;
+
sfe_ipv4_connection_match_compute_translations(original_cm);
sfe_ipv4_connection_match_compute_translations(reply_cm);
sfe_ipv4_insert_connection(si, c);
@@ -1453,14 +1557,38 @@
/*
* We have everything we need!
*/
- DEBUG_INFO("new connection - p: %d\n"
- " s: %s:%pxM(%pxM):%pI4(%pI4):%u(%u)\n"
- " d: %s:%pxM(%pxM):%pI4(%pI4):%u(%u)\n",
+ DEBUG_INFO("NEW connection - p: %d\n"
+ "original_cm: match_dev=src_dev: %s %d %pM\n"
+ " xmit_dev=dest_dev: %s %d %pM\n"
+ " xmit_src_mac: %pM\n"
+ " xmit_dest_mac: %pM\n"
+ " flags: %x l2_hdr: %u\n"
+ "flow_ip: %pI4:%u\n"
+ "flow_ip_xlate: %pI4:%u\n"
+ "flow_mac: %pM\n"
+ "reply_cm: match_dev=dest_dev: %s %d %pM\n"
+ " xmit_dev=src_dev: %s %d %pM\n"
+ " xmit_src_mac: %pM\n"
+ " xmit_dest_mac: %pM\n"
+ " flags: %x l2_hdr: %u\n"
+ "return_ip: %pI4:%u\n"
+ "return_ip_xlate: %pI4:%u\n"
+ "return_mac: %pM\n"
+ "flags: valid=%x src_mac_valid=%x\n",
tuple->protocol,
- src_dev->name, msg->conn_rule.flow_mac, NULL,
- &tuple->flow_ip, &msg->conn_rule.flow_ip_xlate, ntohs(tuple->flow_ident), ntohs(msg->conn_rule.flow_ident_xlate),
- dest_dev->name, NULL, msg->conn_rule.return_mac,
- &tuple->return_ip, &msg->conn_rule.return_ip_xlate, ntohs(tuple->return_ident), ntohs(msg->conn_rule.return_ident_xlate));
+ original_cm->match_dev->name, original_cm->match_dev->ifindex, original_cm->match_dev->dev_addr,
+ original_cm->xmit_dev->name, original_cm->xmit_dev->ifindex, original_cm->xmit_dev->dev_addr,
+ original_cm->xmit_src_mac, original_cm->xmit_dest_mac, original_cm->flags, original_cm->l2_hdr_size,
+ &tuple->flow_ip, ntohs(tuple->flow_ident),
+ &msg->conn_rule.flow_ip_xlate, ntohs(msg->conn_rule.flow_ident_xlate),
+ msg->conn_rule.flow_mac,
+ reply_cm->match_dev->name, reply_cm->match_dev->ifindex, reply_cm->match_dev->dev_addr,
+ reply_cm->xmit_dev->name, reply_cm->xmit_dev->ifindex, reply_cm->xmit_dev->dev_addr,
+ reply_cm->xmit_src_mac, reply_cm->xmit_dest_mac, reply_cm->flags, reply_cm->l2_hdr_size,
+ &tuple->return_ip, ntohs(tuple->return_ident),
+ &msg->conn_rule.return_ip_xlate, ntohs(msg->conn_rule.return_ident_xlate),
+ msg->conn_rule.return_mac,
+ msg->valid_flags, msg->src_mac_rule.mac_valid_flags);
return 0;
}
diff --git a/sfe_ipv4.h b/sfe_ipv4.h
index 91cefae..5535a9c 100644
--- a/sfe_ipv4.h
+++ b/sfe_ipv4.h
@@ -18,6 +18,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef __SFE_IPV4_H
+#define __SFE_IPV4_H
+
#define SFE_IPV4_DSCP_MASK 0x3
#define SFE_IPV4_DSCP_SHIFT 2
@@ -63,6 +66,9 @@
/* Bridge flow */
#define SFE_IPV4_CONNECTION_MATCH_FLAG_MARK (1<<11)
/* skb mark of the packet */
+#define SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG (1<<12)
+ /* Insert VLAN tag */
+
/*
* IPv4 connection matching structure.
*/
@@ -104,6 +110,13 @@
union { /* Protocol-specific state */
struct sfe_ipv4_tcp_connection_match tcp;
} protocol_state;
+
+ /*
+ * VLAN headers
+ */
+ struct sfe_vlan_hdr ingress_vlan_hdr[SFE_MAX_VLAN_DEPTH];
+ struct sfe_vlan_hdr egress_vlan_hdr[SFE_MAX_VLAN_DEPTH];
+
/*
* Stats recorded in a sync period. These stats will be added to
* rx_packet_count64/rx_byte_count64 after a sync period.
@@ -146,6 +159,9 @@
u16 xmit_src_mac[ETH_ALEN / 2];
/* Source MAC address to use when forwarding */
+ u8 ingress_vlan_hdr_cnt; /* Ingress active vlan headers count */
+ u8 egress_vlan_hdr_cnt; /* Egress active vlan headers count */
+
/*
* Summary stats.
*/
@@ -157,6 +173,11 @@
*/
u16 pppoe_session_id;
u8 pppoe_remote_mac[ETH_ALEN];
+
+ /*
+ * Size of all needed L2 headers
+ */
+ u16 l2_hdr_size;
};
/*
@@ -243,6 +264,7 @@
SFE_IPV4_EXCEPTION_EVENT_INVALID_PPPOE_SESSION,
SFE_IPV4_EXCEPTION_EVENT_INCORRECT_PPPOE_PARSING,
SFE_IPV4_EXCEPTION_EVENT_PPPOE_NOT_SET_IN_CME,
+ SFE_IPV4_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH,
SFE_IPV4_EXCEPTION_EVENT_LAST
};
@@ -361,3 +383,5 @@
void sfe_ipv4_exit(void);
int sfe_ipv4_init(void);
+
+#endif /* __SFE_IPV4_H */
diff --git a/sfe_ipv4_tcp.c b/sfe_ipv4_tcp.c
index 1378212..f465dd8 100644
--- a/sfe_ipv4_tcp.c
+++ b/sfe_ipv4_tcp.c
@@ -29,6 +29,7 @@
#include "sfe_flow_cookie.h"
#include "sfe_ipv4.h"
#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
/*
* sfe_ipv4_process_tcp_option_sack()
@@ -132,7 +133,7 @@
bool bridge_flow;
/*
- * Is our packet too short to contain a valid UDP header?
+ * Is our packet too short to contain a valid TCP header?
*/
if (unlikely(!pskb_may_pull(skb, (sizeof(struct tcphdr) + ihl)))) {
sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_TCP_HEADER_INCOMPLETE);
@@ -188,7 +189,7 @@
}
/*
- * If our packet has beern marked as "sync on find" we can't actually
+ * If our packet has been marked as "sync on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before throw it slow path.
*/
@@ -213,6 +214,16 @@
}
#endif
+ /*
+ * Do we expect an ingress VLAN tag for this flow?
+ */
+ if (unlikely(!sfe_vlan_validate_ingress_tag(skb, cm->ingress_vlan_hdr_cnt, cm->ingress_vlan_hdr, l2_info))) {
+ rcu_read_unlock();
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH);
+ DEBUG_TRACE("VLAN tag mismatch. skb=%px\n", skb);
+ return 0;
+ }
+
bridge_flow = !!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_BRIDGE_FLOW);
/*
@@ -238,7 +249,7 @@
sfe_ipv4_sync_status(si, cm->connection, SFE_SYNC_REASON_STATS);
rcu_read_unlock();
- sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_TCP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
DEBUG_TRACE("Larger than MTU\n");
return 0;
}
@@ -502,6 +513,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* From this point on we're good to modify the packet.
*/
@@ -509,20 +530,11 @@
* For PPPoE flows, add PPPoE header before L2 header is added.
*/
if (cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
- if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IP))) {
- rcu_read_unlock();
- DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
- sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
- return 0;
- }
+ sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IP);
this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
}
/*
- * TODO : VLAN headers if any should be added here when supported.
- */
-
- /*
* Update DSCP
*/
if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
@@ -611,7 +623,14 @@
skb->dev = xmit_dev;
/*
- * Check to see if we need to write a header.
+ * Check to see if we need to add VLAN tags
+ */
+ if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG)) {
+ sfe_vlan_add_tag(skb, cm->egress_vlan_hdr_cnt, cm->egress_vlan_hdr);
+ }
+
+ /*
+ * Check to see if we need to write an Ethernet header.
*/
if (likely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
diff --git a/sfe_ipv4_udp.c b/sfe_ipv4_udp.c
index 6266244..0d96df4 100644
--- a/sfe_ipv4_udp.c
+++ b/sfe_ipv4_udp.c
@@ -30,6 +30,7 @@
#include "sfe_flow_cookie.h"
#include "sfe_ipv4.h"
#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
/*
* sfe_ipv4_udp_sk_deliver()
@@ -207,6 +208,16 @@
}
#endif
+ /*
+ * Do we expect an ingress VLAN tag for this flow?
+ */
+ if (unlikely(!sfe_vlan_validate_ingress_tag(skb, cm->ingress_vlan_hdr_cnt, cm->ingress_vlan_hdr, l2_info))) {
+ rcu_read_unlock();
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH);
+ DEBUG_TRACE("VLAN tag mismatch. skb=%px\n", skb);
+ return 0;
+ }
+
bridge_flow = !!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_BRIDGE_FLOW);
/*
@@ -295,6 +306,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* From this point on we're good to modify the packet.
*/
@@ -302,20 +323,11 @@
* For PPPoE flows, add PPPoE header before L2 header is added.
*/
if (cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
- if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IP))) {
- rcu_read_unlock();
- DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
- sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
- return 0;
- }
+ sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IP);
this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
}
/*
- * TODO: VLAN header should be added here when they are supported.
- */
-
- /*
* Enable HW csum if rx checksum is verified and xmit interface is CSUM offload capable.
* Note: If L4 csum at Rx was found to be incorrect, we (router) should use incremental L4 checksum here
* so that HW does not re-calculate/replace the L4 csum
@@ -454,7 +466,14 @@
skb->dev = xmit_dev;
/*
- * Check to see if we need to write a header.
+ * Check to see if we need to add VLAN tags
+ */
+ if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG)) {
+ sfe_vlan_add_tag(skb, cm->egress_vlan_hdr_cnt, cm->egress_vlan_hdr);
+ }
+
+ /*
+ * Check to see if we need to write an Ethernet header.
*/
if (likely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
diff --git a/sfe_ipv6.c b/sfe_ipv6.c
index eab2717..85d7730 100644
--- a/sfe_ipv6.c
+++ b/sfe_ipv6.c
@@ -31,6 +31,7 @@
#include <linux/netfilter.h>
#include <linux/inetdevice.h>
#include <linux/netfilter_ipv6.h>
+
#include "sfe_debug.h"
#include "sfe_api.h"
#include "sfe.h"
@@ -39,6 +40,7 @@
#include "sfe_ipv6_udp.h"
#include "sfe_ipv6_tcp.h"
#include "sfe_ipv6_icmp.h"
+#include "sfe_pppoe.h"
#define sfe_ipv6_addr_copy(src, dest) memcpy((void *)(dest), (void *)(src), 16)
@@ -887,6 +889,59 @@
}
/*
+ * sfe_ipv6_match_entry_set_vlan()
+ */
+static void sfe_ipv6_match_entry_set_vlan(
+ struct sfe_ipv6_connection_match *cm,
+ u32 primary_ingress_vlan_tag,
+ u32 primary_egress_vlan_tag,
+ u32 secondary_ingress_vlan_tag,
+ u32 secondary_egress_vlan_tag)
+{
+ u16 tpid;
+ /*
+ * Prevent stacking header counts when updating.
+ */
+ cm->ingress_vlan_hdr_cnt = 0;
+ cm->egress_vlan_hdr_cnt = 0;
+ memset(cm->ingress_vlan_hdr, 0, sizeof(cm->ingress_vlan_hdr));
+ memset(cm->egress_vlan_hdr, 0, sizeof(cm->egress_vlan_hdr));
+
+ /*
+ * vlan_hdr[0] corresponds to outer tag
+ * vlan_hdr[1] corresponds to inner tag
+ * Extract the vlan information (tpid and tci) from rule message
+ */
+ if ((primary_ingress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(primary_ingress_vlan_tag >> 16);
+ cm->ingress_vlan_hdr[0].tpid = ntohs(tpid);
+ cm->ingress_vlan_hdr[0].tci = (u16)primary_ingress_vlan_tag;
+ cm->ingress_vlan_hdr_cnt++;
+ }
+
+ if ((secondary_ingress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(secondary_ingress_vlan_tag >> 16);
+ cm->ingress_vlan_hdr[1].tpid = ntohs(tpid);
+ cm->ingress_vlan_hdr[1].tci = (u16)secondary_ingress_vlan_tag;
+ cm->ingress_vlan_hdr_cnt++;
+ }
+
+ if ((primary_egress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(primary_egress_vlan_tag >> 16);
+ cm->egress_vlan_hdr[0].tpid = ntohs(tpid);
+ cm->egress_vlan_hdr[0].tci = (u16)primary_egress_vlan_tag;
+ cm->egress_vlan_hdr_cnt++;
+ }
+
+ if ((secondary_egress_vlan_tag & VLAN_VID_MASK) != SFE_VLAN_ID_NOT_CONFIGURED) {
+ tpid = (u16)(secondary_egress_vlan_tag >> 16);
+ cm->egress_vlan_hdr[1].tpid = ntohs(tpid);
+ cm->egress_vlan_hdr[1].tci = (u16)secondary_egress_vlan_tag;
+ cm->egress_vlan_hdr_cnt++;
+ }
+}
+
+/*
* sfe_ipv6_update_rule()
* update forwarding rule after rule is created.
*/
@@ -1027,8 +1082,12 @@
* Check to see if there is already a flow that matches the rule we're
* trying to create. If there is then we can't create a new one.
*/
- old_c = sfe_ipv6_find_connection(si, tuple->protocol, (struct sfe_ipv6_addr *)tuple->flow_ip, tuple->flow_ident,
- (struct sfe_ipv6_addr *)tuple->return_ip, tuple->return_ident);
+ old_c = sfe_ipv6_find_connection(si,
+ tuple->protocol,
+ (struct sfe_ipv6_addr *)tuple->flow_ip,
+ tuple->flow_ident,
+ (struct sfe_ipv6_addr *)tuple->return_ip,
+ tuple->return_ident);
if (old_c != NULL) {
this_cpu_inc(si->stats_pcpu->connection_create_collisions64);
@@ -1083,13 +1142,14 @@
original_cm->connection = c;
original_cm->counter_match = reply_cm;
+ original_cm->l2_hdr_size = 0;
+ original_cm->flags = 0;
/*
* Valid in decap direction only
*/
RCU_INIT_POINTER(original_cm->up, NULL);
- original_cm->flags = 0;
if (msg->valid_flags & SFE_RULE_CREATE_MARK_VALID) {
original_cm->mark = msg->mark_rule.flow_mark;
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_MARK;
@@ -1098,6 +1158,7 @@
original_cm->priority = msg->qos_rule.flow_qos_tag;
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PRIORITY_REMARK;
}
+
if (msg->valid_flags & SFE_RULE_CREATE_DSCP_MARKING_VALID) {
original_cm->dscp = msg->dscp_rule.flow_dscp << SFE_IPV6_DSCP_SHIFT;
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK;
@@ -1106,6 +1167,25 @@
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_BRIDGE_FLOW;
}
+ /*
+ * Add VLAN rule to original_cm
+ */
+ if (msg->valid_flags & SFE_RULE_CREATE_VLAN_VALID) {
+ struct sfe_vlan_rule *vlan_primary_rule = &msg->vlan_primary_rule;
+ struct sfe_vlan_rule *vlan_secondary_rule = &msg->vlan_secondary_rule;
+ sfe_ipv6_match_entry_set_vlan(original_cm,
+ vlan_primary_rule->ingress_vlan_tag,
+ vlan_primary_rule->egress_vlan_tag,
+ vlan_secondary_rule->ingress_vlan_tag,
+ vlan_secondary_rule->egress_vlan_tag);
+
+ if ((msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE) &&
+ original_cm->egress_vlan_hdr_cnt > 0) {
+ original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG;
+ original_cm->l2_hdr_size += original_cm->egress_vlan_hdr_cnt * VLAN_HLEN;
+ }
+ }
+
#ifdef CONFIG_NF_FLOW_COOKIE
original_cm->flow_cookie = 0;
#endif
@@ -1129,6 +1209,7 @@
}
}
+ reply_cm->l2_hdr_size = 0;
reply_cm->flags = 0;
/*
@@ -1149,12 +1230,14 @@
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
+ reply_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
reply_cm->pppoe_session_id = msg->pppoe_rule.flow_pppoe_session_id;
ether_addr_copy(reply_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
}
if (msg->valid_flags & SFE_RULE_CREATE_PPPOE_ENCAP_VALID) {
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
+ original_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
original_cm->pppoe_session_id = msg->pppoe_rule.return_pppoe_session_id;
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.return_pppoe_remote_mac);
@@ -1186,6 +1269,7 @@
ether_addr_copy((u8 *)original_cm->xmit_dest_mac, (u8 *)msg->conn_rule.return_mac);
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR;
+ original_cm->l2_hdr_size += ETH_HLEN;
/*
* If our dev writes Ethernet headers then we can write a really fast
@@ -1282,6 +1366,7 @@
#else
if (!refcount_inc_not_zero(&sk->sk_refcnt)) {
#endif
+ spin_unlock_bh(&si->lock);
kfree(reply_cm);
kfree(original_cm);
kfree(c);
@@ -1312,6 +1397,25 @@
ntohs(reply_cm->match_dest_port), ntohs(reply_cm->xlate_dest_port));
}
+ /*
+ * Add VLAN rule to reply_cm
+ */
+ if (msg->valid_flags & SFE_RULE_CREATE_VLAN_VALID) {
+ struct sfe_vlan_rule *vlan_primary_rule = &msg->vlan_primary_rule;
+ struct sfe_vlan_rule *vlan_secondary_rule = &msg->vlan_secondary_rule;
+ sfe_ipv6_match_entry_set_vlan(reply_cm,
+ vlan_primary_rule->egress_vlan_tag,
+ vlan_primary_rule->ingress_vlan_tag,
+ vlan_secondary_rule->egress_vlan_tag,
+ vlan_secondary_rule->ingress_vlan_tag);
+
+ if ((msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE) &&
+ reply_cm->egress_vlan_hdr_cnt > 0) {
+ reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG;
+ reply_cm->l2_hdr_size += reply_cm->egress_vlan_hdr_cnt * VLAN_HLEN;
+ }
+ }
+
#ifdef CONFIG_NF_FLOW_COOKIE
reply_cm->flow_cookie = 0;
#endif
@@ -1359,6 +1463,7 @@
ether_addr_copy((u8 *)reply_cm->xmit_dest_mac, (u8 *)msg->conn_rule.flow_mac);
reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR;
+ reply_cm->l2_hdr_size += ETH_HLEN;
/*
* If our dev writes Ethernet headers then we can write a really fast
@@ -1375,25 +1480,6 @@
* No support for NAT in ipv6
*/
- c->protocol = tuple->protocol;
- c->src_ip[0] = *(struct sfe_ipv6_addr *)tuple->flow_ip;
- c->src_ip_xlate[0] = *(struct sfe_ipv6_addr *)tuple->flow_ip;
- c->src_port = tuple->flow_ident;
- c->src_port_xlate = tuple->flow_ident;
- c->original_dev = src_dev;
- c->original_match = original_cm;
-
- c->dest_ip[0] = *(struct sfe_ipv6_addr *)tuple->return_ip;
- c->dest_ip_xlate[0] = *(struct sfe_ipv6_addr *)tuple->return_ip;
- c->dest_port = tuple->return_ident;
- c->dest_port_xlate = tuple->return_ident;
-
- c->reply_dev = dest_dev;
- c->reply_match = reply_cm;
- c->debug_read_seq = 0;
- c->last_sync_jiffies = get_jiffies_64();
- c->removed = false;
-
/*
* Initialize the protocol-specific information that we track.
*/
@@ -1414,6 +1500,28 @@
break;
}
+ /*
+ * Fill in the ipv6_connection object.
+ */
+ c->protocol = tuple->protocol;
+ c->src_ip[0] = *(struct sfe_ipv6_addr *)tuple->flow_ip;
+ c->src_ip_xlate[0] = *(struct sfe_ipv6_addr *)tuple->flow_ip;
+ c->src_port = tuple->flow_ident;
+ c->src_port_xlate = tuple->flow_ident;
+ c->original_dev = src_dev;
+ c->original_match = original_cm;
+
+ c->dest_ip[0] = *(struct sfe_ipv6_addr *)tuple->return_ip;
+ c->dest_ip_xlate[0] = *(struct sfe_ipv6_addr *)tuple->return_ip;
+ c->dest_port = tuple->return_ident;
+ c->dest_port_xlate = tuple->return_ident;
+
+ c->reply_dev = dest_dev;
+ c->reply_match = reply_cm;
+ c->debug_read_seq = 0;
+ c->last_sync_jiffies = get_jiffies_64();
+ c->removed = false;
+
sfe_ipv6_connection_match_compute_translations(original_cm);
sfe_ipv6_connection_match_compute_translations(reply_cm);
sfe_ipv6_insert_connection(si, c);
@@ -2175,7 +2283,7 @@
}
/*
- * sfe_ipv4_set_cpu()
+ * sfe_ipv6_set_cpu()
*/
static ssize_t sfe_ipv6_set_cpu(struct device *dev,
struct device_attribute *attr,
diff --git a/sfe_ipv6.h b/sfe_ipv6.h
index 002ccaa..acaa2dd 100644
--- a/sfe_ipv6.h
+++ b/sfe_ipv6.h
@@ -18,6 +18,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef __SFE_IPV6_H
+#define __SFE_IPV6_H
+
#define CHAR_DEV_MSG_SIZE 768
#define SFE_IPV6_DSCP_MASK 0xf03f
@@ -76,6 +79,9 @@
/* Bridge flow */
#define SFE_IPV6_CONNECTION_MATCH_FLAG_MARK (1<<11)
/* set skb mark*/
+#define SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG (1<<12)
+ /* Insert VLAN tag */
+
/*
* IPv6 connection matching structure.
*/
@@ -115,6 +121,13 @@
union { /* Protocol-specific state */
struct sfe_ipv6_tcp_connection_match tcp;
} protocol_state;
+
+ /*
+ * VLAN headers
+ */
+ struct sfe_vlan_hdr ingress_vlan_hdr[SFE_MAX_VLAN_DEPTH];
+ struct sfe_vlan_hdr egress_vlan_hdr[SFE_MAX_VLAN_DEPTH];
+
/*
* Stats recorded in a sync period. These stats will be added to
* rx_packet_count64/rx_byte_count64 after a sync period.
@@ -152,6 +165,9 @@
u16 xmit_src_mac[ETH_ALEN / 2];
/* Source MAC address to use when forwarding */
+ u8 ingress_vlan_hdr_cnt; /* Ingress active vlan headers count */
+ u8 egress_vlan_hdr_cnt; /* Egress active vlan headers count */
+
/*
* Summary stats.
*/
@@ -163,6 +179,11 @@
*/
u16 pppoe_session_id;
u8 pppoe_remote_mac[ETH_ALEN];
+
+ /*
+ * Size of all needed L2 headers
+ */
+ u16 l2_hdr_size;
};
/*
@@ -248,6 +269,8 @@
SFE_IPV6_EXCEPTION_EVENT_INVALID_PPPOE_SESSION,
SFE_IPV6_EXCEPTION_EVENT_INCORRECT_PPPOE_PARSING,
SFE_IPV6_EXCEPTION_EVENT_PPPOE_NOT_SET_IN_CME,
+ SFE_IPV6_EXCEPTION_EVENT_NOT_ENOUGH_HEADROOM,
+ SFE_IPV6_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH,
SFE_IPV6_EXCEPTION_EVENT_LAST
};
@@ -396,3 +419,5 @@
void sfe_ipv6_exit(void);
int sfe_ipv6_init(void);
+
+#endif /* __SFE_IPV6_H */
diff --git a/sfe_ipv6_tcp.c b/sfe_ipv6_tcp.c
index 5d43493..85c299b 100644
--- a/sfe_ipv6_tcp.c
+++ b/sfe_ipv6_tcp.c
@@ -29,6 +29,7 @@
#include "sfe_flow_cookie.h"
#include "sfe_ipv6.h"
#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
/*
* sfe_ipv6_process_tcp_option_sack()
@@ -131,7 +132,7 @@
bool bridge_flow;
/*
- * Is our packet too short to contain a valid UDP header?
+ * Is our packet too short to contain a valid TCP header?
*/
if (!pskb_may_pull(skb, (sizeof(struct tcphdr) + ihl))) {
@@ -190,7 +191,7 @@
}
/*
- * If our packet has beern marked as "sync on find" we can't actually
+ * If our packet has been marked as "sync on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before throw it slow path.
*/
@@ -215,6 +216,16 @@
}
#endif
+ /*
+ * Do we expect an ingress VLAN tag for this flow?
+ */
+ if (unlikely(!sfe_vlan_validate_ingress_tag(skb, cm->ingress_vlan_hdr_cnt, cm->ingress_vlan_hdr, l2_info))) {
+ rcu_read_unlock();
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH);
+ DEBUG_TRACE("VLAN tag mismatch. skb=%px\n", skb);
+ return 0;
+ }
+
bridge_flow = !!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_BRIDGE_FLOW);
/*
@@ -512,6 +523,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* From this point on we're good to modify the packet.
*/
@@ -519,19 +540,11 @@
* For PPPoE flows, add PPPoE header before L2 header is added.
*/
if (cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
- if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IPV6))) {
- rcu_read_unlock();
- DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
- sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
- }
+ sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IPV6);
this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
}
/*
- * TODO: VLAN header should be added here when they are supported.
- */
-
- /*
* Update DSCP
*/
if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
@@ -614,8 +627,16 @@
xmit_dev = cm->xmit_dev;
skb->dev = xmit_dev;
+
/*
- * Check to see if we need to write a header.
+ * Check to see if we need to add VLAN tags
+ */
+ if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG)) {
+ sfe_vlan_add_tag(skb, cm->egress_vlan_hdr_cnt, cm->egress_vlan_hdr);
+ }
+
+ /*
+ * Check to see if we need to write an Ethernet header.
*/
if (likely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
diff --git a/sfe_ipv6_udp.c b/sfe_ipv6_udp.c
index e5eeb9e..fc260bd 100644
--- a/sfe_ipv6_udp.c
+++ b/sfe_ipv6_udp.c
@@ -30,6 +30,7 @@
#include "sfe_flow_cookie.h"
#include "sfe_ipv6.h"
#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
/*
* sfe_ipv6_udp_sk_deliver()
@@ -190,6 +191,16 @@
}
/*
+ * Do we expect an ingress VLAN tag for this flow?
+ */
+ if (unlikely(!sfe_vlan_validate_ingress_tag(skb, cm->ingress_vlan_hdr_cnt, cm->ingress_vlan_hdr, l2_info))) {
+ rcu_read_unlock();
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH);
+ DEBUG_TRACE("VLAN tag mismatch. skb=%px\n", skb);
+ return 0;
+ }
+
+ /*
* If our packet has been marked as "flush on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before exception it to slow path.
@@ -303,6 +314,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* From this point on we're good to modify the packet.
*/
@@ -310,19 +331,11 @@
* For PPPoE flows, add PPPoE header before L2 header is added.
*/
if (cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
- if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IPV6))) {
- rcu_read_unlock();
- DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
- sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
- }
+ sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IPV6);
this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
}
/*
- * TODO: VLAN header should be added here when they are supported.
- */
-
- /*
* UDP sock will be valid only in decap-path.
* Call encap_rcv function associated with udp_sock in cm.
*/
@@ -447,7 +460,14 @@
skb->dev = xmit_dev;
/*
- * Check to see if we need to write a header.
+ * Check to see if we need to add VLAN tags
+ */
+ if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG)) {
+ sfe_vlan_add_tag(skb, cm->egress_vlan_hdr_cnt, cm->egress_vlan_hdr);
+ }
+
+ /*
+ * Check to see if we need to write an Ethernet header.
*/
if (likely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
diff --git a/sfe_pppoe.c b/sfe_pppoe.c
index 4f9341e..4b20311 100644
--- a/sfe_pppoe.c
+++ b/sfe_pppoe.c
@@ -32,7 +32,7 @@
*
* skb->data will point to PPPoE header after the function
*/
-bool sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol)
+void sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol)
{
u16 *l2_header;
struct pppoe_hdr *ph;
@@ -40,15 +40,6 @@
u16 *l3_header = (u16 *)skb->data;
/*
- * Check that we have space for PPPoE header, PPP header (2 bytes) and Ethernet header
- * TODO: Calculate the l2_hdr_len during rule push so that this is avoided in datapath
- */
- if (unlikely(skb_headroom(skb) < ((sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)) + ETH_HLEN))) {
- DEBUG_TRACE("%px: Not enough headroom for PPPoE header\n", skb);
- return false;
- }
-
- /*
* PPPoE header (6 bytes) + PPP header (2 bytes)
*
* Hence move by 8 bytes to accomodate PPPoE header
@@ -80,8 +71,6 @@
* L2 header offset will point to PPPoE header,
*/
__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
-
- return true;
}
/*
diff --git a/sfe_pppoe.h b/sfe_pppoe.h
index 4190b4e..c797dc7 100644
--- a/sfe_pppoe.h
+++ b/sfe_pppoe.h
@@ -17,6 +17,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef __SFE_PPPOE_H
+#define __SFE_PPPOE_H
+
#include <linux/ppp_defs.h>
#include <linux/if_pppox.h>
@@ -24,5 +27,12 @@
u16 protocol;
};
-bool sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol);
+/*
+ * PPPoE (6-byte) + PPP (2-byte) header.
+ */
+#define SFE_PPPOE_SESSION_HEADER_SIZE 8
+
+void sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol);
bool sfe_pppoe_parse_hdr(struct sk_buff *skb, struct sfe_l2_info *l2_info);
+
+#endif /* __SFE_PPPOE_H */
diff --git a/sfe_vlan.h b/sfe_vlan.h
new file mode 100644
index 0000000..505b24b
--- /dev/null
+++ b/sfe_vlan.h
@@ -0,0 +1,217 @@
+/*
+ * sfe_vlan.h
+ * Shortcut flow acceleration for 802.1AD/802.1Q flow
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SFE_VLAN_H
+#define __SFE_VLAN_H
+
+#include <linux/if_vlan.h>
+
+/*
+ * sfe_vlan_check_and_parse_tag()
+ *
+ * case 1: QinQ frame (e.g. outer tag = 88a80032, inner tag = 81000001):
+ * When entering this function:
+ * ----+-----------------+-----|-----+-----------+-----+---------
+ * |DMAC |SMAC |88|a8|00|32|81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----A-----+-----------+-----+---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_8021AD)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * When exiting:
+ * ----+-----------------+-----------+-----------+-----+---------
+ * |DMAC |SMAC |88|a8|00|32|81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----------+-----------+-----A---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * l2_info->vlan_hdr_cnt = 2
+ * l2_info->vlan_hdr[0].tpid = ntohs(ETH_P_8021AD)
+ * l2_info->vlan_hdr[0].tci = 0x0032
+ * l2_info->vlan_hdr[1].tpid = ntohs(ETH_P_8021Q)
+ * l2_info->vlan_hdr[1].tci = 0x0001
+ * l2_info->protocol = ETH_P_IP
+ *
+ * case 2: 802.1Q frame (e.g. the tag is 81000001):
+ * When entering this function:
+ * ----+-----------------+-----|-----+-----+---------
+ * |DMAC |SMAC |81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----A-----+-----+---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_8021Q)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * When exiting:
+ * ----+-----------------+-----------+-----+---------
+ * |DMAC |SMAC |81|00|00|01|08|00|45|00|
+ * ----+-----------------+-----------+-----A---------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * l2_info->vlan_hdr_cnt = 1
+ * l2_info->vlan_hdr[0].tpid = ntohs(ETH_P_8021Q)
+ * l2_info->vlan_hdr[0].tci = 0x0001
+ * l2_info->protocol = ETH_P_IP
+ *
+ * case 3: untagged frame
+ * When entering this function:
+ * ----+-----------------+-----|---------------------
+ * |DMAC |SMAC |08|00|45|00|
+ * ----+-----------------+-----A---------------------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * When exiting:
+ * ----+-----------------+-----|---------------------
+ * |DMAC |SMAC |08|00|45|00|
+ * ----+-----------------+-----A---------------------
+ * skb->data
+ * skb->protocol = ntohs(ETH_P_IP)
+ * skb->vlan_proto = 0
+ * skb->vlan_tci = 0
+ * skb->vlan_present = 0
+ * l2_info->vlan_hdr_cnt = 0
+ * l2_info->protocol = ETH_P_IP
+ */
+static inline bool sfe_vlan_check_and_parse_tag(struct sk_buff *skb, struct sfe_l2_info *l2_info)
+{
+ struct vlan_hdr *vhdr;
+
+ l2_info->vlan_hdr_cnt = 0;
+
+ while ((skb->protocol == htons(ETH_P_8021AD) || skb->protocol == htons(ETH_P_8021Q)) &&
+ l2_info->vlan_hdr_cnt < SFE_MAX_VLAN_DEPTH) {
+ if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) {
+ return false;
+ }
+ vhdr = (struct vlan_hdr *)skb->data;
+ l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tpid = skb->protocol;
+ l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tci = ntohs(vhdr->h_vlan_TCI);
+ skb->protocol = vhdr->h_vlan_encapsulated_proto;
+ l2_info->vlan_hdr_cnt++;
+ __skb_pull(skb, VLAN_HLEN);
+ }
+
+ l2_info->protocol = htons(skb->protocol);
+ return true;
+}
+
+/*
+ * sfe_vlan_undo_parse()
+ * Restore some skb fields which are modified when parsing VLAN tags.
+ */
+static inline void sfe_vlan_undo_parse(struct sk_buff *skb, struct sfe_l2_info *l2_info)
+{
+ if (l2_info->vlan_hdr_cnt == 0) {
+ return;
+ }
+
+ skb->protocol = l2_info->vlan_hdr[0].tpid;
+ __skb_push(skb, l2_info->vlan_hdr_cnt * VLAN_HLEN);
+}
+
+/*
+ * sfe_vlan_validate_ingress_tag()
+ * Validate ingress packet's VLAN tag
+ */
+static inline bool sfe_vlan_validate_ingress_tag(
+ struct sk_buff *skb, u8 count, struct sfe_vlan_hdr *vlan_hdr, struct sfe_l2_info *l2_info)
+{
+ u8 i;
+
+ if (likely(!sfe_is_l2_feature_enabled())) {
+ return true;
+ }
+
+ if (unlikely(count != l2_info->vlan_hdr_cnt)) {
+ return false;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (unlikely(vlan_hdr[i].tpid != l2_info->vlan_hdr[i].tpid)) {
+ return false;
+ }
+
+ if (unlikely((vlan_hdr[i].tci & VLAN_VID_MASK) !=
+ (l2_info->vlan_hdr[i].tci & VLAN_VID_MASK))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * sfe_vlan_add_tag()
+ * Add VLAN tags at skb->data.
+ * Normally, it is called just before adding 14-byte Ethernet header.
+ *
+ * This function does not update skb->mac_header so later code
+ * needs to call skb_reset_mac_header()/skb_reset_mac_len() to
+ * get correct skb->mac_header/skb->mac_len.
+ *
+ * It assumes:
+ * - skb->protocol is set
+ * - skb has enough headroom to write VLAN tags
+ * - 0 < count <= SFE_MAX_VLAN_DEPTH
+ *
+ * When entering (e.g. skb->protocol = ntohs(ETH_P_IP) or ntohs(ETH_P_PPP_SES)):
+ * -------------------------------+---------------------
+ * |45|00|...
+ * -------------------------------A---------------------
+ * skb->data
+ * -------------------------------v-----------------+-----+----------
+ * |11|00|xx|xx|xx|xx|00|21|45|00|...
+ * -------------------------------+-----------------+-----+----------
+ *
+ * When exiting (e.g. to add outer/inner tag = 88a80032/81000001):
+ * -------------+-----------+-----+---------------------
+ * |00|32|81|00|00|01|08|00|45|00|05|d8|....
+ * -------A-----+-----------+-----+---------------------
+ * skb->data
+ * -------v-----+-----------+-----+-----------------+-----+----------
+ * |00|32|81|00|00|01|88|64|11|00|xx|xx|xx|xx|00|21|45|00|
+ * -------------+-----------+-----+-----------------+-----+----------
+ * skb->protocol = ntohs(ETH_P_8021AD)
+ */
+static inline void sfe_vlan_add_tag(struct sk_buff *skb, int count, struct sfe_vlan_hdr *vlan)
+{
+ struct vlan_hdr *vhdr;
+ int i;
+ vlan += (count - 1);
+
+ for (i = 0; i < count; i++) {
+ skb_push(skb, VLAN_HLEN);
+ vhdr = (struct vlan_hdr *)skb->data;
+ vhdr->h_vlan_TCI = htons(vlan->tci);
+ vhdr->h_vlan_encapsulated_proto = skb->protocol;
+ skb->protocol = vlan->tpid;
+ vlan--;
+ }
+}
+
+#endif /* __SFE_VLAN_H */