Commit 3d93a144 authored by David S. Miller's avatar David S. Miller

Merge branch 'vln-ocelot-fixes'

Vladimir Oltean says:

====================
VLAN fixes for Ocelot driver

This is a collection of patches I've gathered over the past several
months.

Patches 1-6/14 are supporting patches for selftests.

Patch 9/14 fixes PTP TX from a VLAN upper of a VLAN-aware bridge port
when using the "ocelot-8021q" tagging protocol. Patch 7/14 is its
supporting selftest.

Patch 10/14 fixes the QoS class used by PTP in the same case as above.
It is hard to quantify - there is no selftest.

Patch 11/14 fixes potential data corruption during PTP TX in the same
case as above. Again, there is no selftest.

Patch 13/14 fixes RX in the same case as above - 8021q upper of a
VLAN-aware bridge port, with the "ocelot-8021q" tagging protocol. Patch
12/14 is a supporting patch for this in the DSA core, and 7/14 is also
its selftest.

Patch 14/14 ensures that VLAN-aware bridges offloaded to Ocelot only
react to the ETH_P_8021Q TPID, and treat absolutely everything else as
VLAN-untagged, including ETH_P_8021AD. Patch 8/14 is the supporting
selftest.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b153b3c7 36dd1141
......@@ -61,11 +61,46 @@ static int felix_cpu_port_for_conduit(struct dsa_switch *ds,
return cpu_dp->index;
}
/**
* felix_update_tag_8021q_rx_rule - Update VCAP ES0 tag_8021q rule after
* vlan_filtering change
* @outer_tagging_rule: Pointer to VCAP filter on which the update is performed
* @vlan_filtering: Current bridge VLAN filtering setting
*
* Source port identification for tag_8021q is done using VCAP ES0 rules on the
* CPU port(s). The ES0 tag B (inner tag from the packet) can be configured as
* either:
* - push_inner_tag=0: the inner tag is never pushed into the frame
* (and we lose info about the classified VLAN). This is
* good when the classified VLAN is a discardable quantity
* for the software RX path: it is either set to
* OCELOT_STANDALONE_PVID, or to
* ocelot_vlan_unaware_pvid(bridge).
* - push_inner_tag=1: the inner tag is always pushed. This is good when the
* classified VLAN is not a discardable quantity (the port
* is under a VLAN-aware bridge, and software needs to
* continue processing the packet in the same VLAN as the
* hardware).
* The point is that what is good for a VLAN-unaware port is not good for a
* VLAN-aware port, and vice versa. Thus, the RX tagging rules must be kept in
* sync with the VLAN filtering state of the port.
*/
static void
felix_update_tag_8021q_rx_rule(struct ocelot_vcap_filter *outer_tagging_rule,
bool vlan_filtering)
{
if (vlan_filtering)
outer_tagging_rule->action.push_inner_tag = OCELOT_ES0_TAG;
else
outer_tagging_rule->action.push_inner_tag = OCELOT_NO_ES0_TAG;
}
/* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that
* the tagger can perform RX source port identification.
*/
static int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port,
int upstream, u16 vid)
int upstream, u16 vid,
bool vlan_filtering)
{
struct ocelot_vcap_filter *outer_tagging_rule;
struct ocelot *ocelot = ds->priv;
......@@ -96,6 +131,14 @@ static int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port,
outer_tagging_rule->action.tag_a_tpid_sel = OCELOT_TAG_TPID_SEL_8021AD;
outer_tagging_rule->action.tag_a_vid_sel = 1;
outer_tagging_rule->action.vid_a_val = vid;
felix_update_tag_8021q_rx_rule(outer_tagging_rule, vlan_filtering);
outer_tagging_rule->action.tag_b_tpid_sel = OCELOT_TAG_TPID_SEL_8021Q;
/* Leave TAG_B_VID_SEL at 0 (Classified VID + VID_B_VAL). Since we also
* leave VID_B_VAL at 0, this makes ES0 tag B (the inner tag) equal to
* the classified VID, which we need to see in the DSA tagger's receive
* path. Note: the inner tag is only visible in the packet when pushed
* (push_inner_tag == OCELOT_ES0_TAG).
*/
err = ocelot_vcap_filter_add(ocelot, outer_tagging_rule, NULL);
if (err)
......@@ -227,6 +270,7 @@ static int felix_tag_8021q_vlan_del_tx(struct dsa_switch *ds, int port, u16 vid)
static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
u16 flags)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct dsa_port *cpu_dp;
int err;
......@@ -234,11 +278,12 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
* membership, which we aren't. So we don't need to add any VCAP filter
* for the CPU port.
*/
if (!dsa_is_user_port(ds, port))
if (!dsa_port_is_user(dp))
return 0;
dsa_switch_for_each_cpu_port(cpu_dp, ds) {
err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);
err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid,
dsa_port_is_vlan_filtering(dp));
if (err)
return err;
}
......@@ -258,10 +303,11 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct dsa_port *cpu_dp;
int err;
if (!dsa_is_user_port(ds, port))
if (!dsa_port_is_user(dp))
return 0;
dsa_switch_for_each_cpu_port(cpu_dp, ds) {
......@@ -278,9 +324,39 @@ static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
del_tx_failed:
dsa_switch_for_each_cpu_port(cpu_dp, ds)
felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);
felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid,
dsa_port_is_vlan_filtering(dp));
return err;
}
static int felix_update_tag_8021q_rx_rules(struct dsa_switch *ds, int port,
bool vlan_filtering)
{
struct ocelot_vcap_filter *outer_tagging_rule;
struct ocelot_vcap_block *block_vcap_es0;
struct ocelot *ocelot = ds->priv;
struct dsa_port *cpu_dp;
unsigned long cookie;
int err;
block_vcap_es0 = &ocelot->block[VCAP_ES0];
dsa_switch_for_each_cpu_port(cpu_dp, ds) {
cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port,
cpu_dp->index);
outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
cookie, false);
felix_update_tag_8021q_rx_rule(outer_tagging_rule, vlan_filtering);
err = ocelot_vcap_filter_replace(ocelot, outer_tagging_rule);
if (err)
return err;
}
return 0;
}
static int felix_trap_get_cpu_port(struct dsa_switch *ds,
......@@ -528,7 +604,19 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
* so we need to be careful that there are no extra frames to be
* dequeued over MMIO, since we would never know to discard them.
*/
ocelot_lock_xtr_grp_bh(ocelot, 0);
ocelot_drain_cpu_queue(ocelot, 0);
ocelot_unlock_xtr_grp_bh(ocelot, 0);
/* Problem: when using push_inner_tag=1 for ES0 tag B, we lose info
* about whether the received packets were VLAN-tagged on the wire,
* since they are always tagged on egress towards the CPU port.
*
* Since using push_inner_tag=1 is unavoidable for VLAN-aware bridges,
* we must work around the fallout by untagging in software to make
* untagged reception work more or less as expected.
*/
ds->untag_vlan_aware_bridge_pvid = true;
return 0;
}
......@@ -554,6 +642,8 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds)
ocelot_port_teardown_dsa_8021q_cpu(ocelot, dp->index);
dsa_tag_8021q_unregister(ds);
ds->untag_vlan_aware_bridge_pvid = false;
}
static unsigned long felix_tag_8021q_get_host_fwd_mask(struct dsa_switch *ds)
......@@ -1008,8 +1098,23 @@ static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;
bool using_tag_8021q;
struct felix *felix;
int err;
return ocelot_port_vlan_filtering(ocelot, port, enabled, extack);
err = ocelot_port_vlan_filtering(ocelot, port, enabled, extack);
if (err)
return err;
felix = ocelot_to_felix(ocelot);
using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q;
if (using_tag_8021q) {
err = felix_update_tag_8021q_rx_rules(ds, port, enabled);
if (err)
return err;
}
return 0;
}
static int felix_vlan_add(struct dsa_switch *ds, int port,
......@@ -1518,6 +1623,8 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
int port = xmit_work->dp->index;
int retries = 10;
ocelot_lock_inj_grp(ocelot, 0);
do {
if (ocelot_can_inject(ocelot, 0))
break;
......@@ -1526,6 +1633,7 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
} while (--retries);
if (!retries) {
ocelot_unlock_inj_grp(ocelot, 0);
dev_err(ocelot->dev, "port %d failed to inject skb\n",
port);
ocelot_port_purge_txtstamp_skb(ocelot, port, skb);
......@@ -1535,6 +1643,8 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
ocelot_unlock_inj_grp(ocelot, 0);
consume_skb(skb);
kfree(xmit_work);
}
......@@ -1694,6 +1804,8 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot)
if (!felix->info->quirk_no_xtr_irq)
return false;
ocelot_lock_xtr_grp(ocelot, grp);
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
struct sk_buff *skb;
unsigned int type;
......@@ -1730,6 +1842,8 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot)
ocelot_drain_cpu_queue(ocelot, 0);
}
ocelot_unlock_xtr_grp(ocelot, grp);
return true;
}
......
......@@ -453,8 +453,157 @@ static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
return VLAN_N_VID - bridge_num - 1;
}
/**
* ocelot_update_vlan_reclassify_rule() - Make switch aware only to bridge VLAN TPID
*
* @ocelot: Switch private data structure
* @port: Index of ingress port
*
* IEEE 802.1Q-2018 clauses "5.5 C-VLAN component conformance" and "5.6 S-VLAN
* component conformance" suggest that a C-VLAN component should only recognize
* and filter on C-Tags, and an S-VLAN component should only recognize and
* process based on C-Tags.
*
* In Linux, as per commit 1a0b20b25732 ("Merge branch 'bridge-next'"), C-VLAN
* components are largely represented by a bridge with vlan_protocol 802.1Q,
* and S-VLAN components by a bridge with vlan_protocol 802.1ad.
*
* Currently the driver only offloads vlan_protocol 802.1Q, but the hardware
* design is non-conformant, because the switch assigns each frame to a VLAN
* based on an entirely different question, as detailed in figure "Basic VLAN
* Classification Flow" from its manual and reproduced below.
*
* Set TAG_TYPE, PCP, DEI, VID to port-default values in VLAN_CFG register
* if VLAN_AWARE_ENA[port] and frame has outer tag then:
* if VLAN_INNER_TAG_ENA[port] and frame has inner tag then:
* TAG_TYPE = (Frame.InnerTPID <> 0x8100)
* Set PCP, DEI, VID to values from inner VLAN header
* else:
* TAG_TYPE = (Frame.OuterTPID <> 0x8100)
* Set PCP, DEI, VID to values from outer VLAN header
* if VID == 0 then:
* VID = VLAN_CFG.VLAN_VID
*
* Summarized, the switch will recognize both 802.1Q and 802.1ad TPIDs as VLAN
* "with equal rights", and just set the TAG_TYPE bit to 0 (if 802.1Q) or to 1
* (if 802.1ad). It will classify based on whichever of the tags is "outer", no
* matter what TPID that may have (or "inner", if VLAN_INNER_TAG_ENA[port]).
*
* In the VLAN Table, the TAG_TYPE information is not accessible - just the
* classified VID is - so it is as if each VLAN Table entry is for 2 VLANs:
* C-VLAN X, and S-VLAN X.
*
* Whereas the Linux bridge behavior is to only filter on frames with a TPID
* equal to the vlan_protocol, and treat everything else as VLAN-untagged.
*
* Consider an ingress packet tagged with 802.1ad VID=3 and 802.1Q VID=5,
* received on a bridge vlan_filtering=1 vlan_protocol=802.1Q port. This frame
* should be treated as 802.1Q-untagged, and classified to the PVID of that
* bridge port. Not to VID=3, and not to VID=5.
*
* The VCAP IS1 TCAM has everything we need to overwrite the choices made in
* the basic VLAN classification pipeline: it can match on TAG_TYPE in the key,
* and it can modify the classified VID in the action. Thus, for each port
* under a vlan_filtering bridge, we can insert a rule in VCAP IS1 lookup 0 to
* match on 802.1ad tagged frames and modify their classified VID to the 802.1Q
* PVID of the port. This effectively makes it appear to the outside world as
* if those packets were processed as VLAN-untagged.
*
* The rule needs to be updated each time the bridge PVID changes, and needs
* to be deleted if the bridge PVID is deleted, or if the port becomes
* VLAN-unaware.
*/
static int ocelot_update_vlan_reclassify_rule(struct ocelot *ocelot, int port)
{
unsigned long cookie = OCELOT_VCAP_IS1_VLAN_RECLASSIFY(ocelot, port);
struct ocelot_vcap_block *block_vcap_is1 = &ocelot->block[VCAP_IS1];
struct ocelot_port *ocelot_port = ocelot->ports[port];
const struct ocelot_bridge_vlan *pvid_vlan;
struct ocelot_vcap_filter *filter;
int err, val, pcp, dei;
bool vid_replace_ena;
u16 vid;
pvid_vlan = ocelot_port->pvid_vlan;
vid_replace_ena = ocelot_port->vlan_aware && pvid_vlan;
filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, cookie,
false);
if (!vid_replace_ena) {
/* If the reclassification filter doesn't need to exist, delete
* it if it was previously installed, and exit doing nothing
* otherwise.
*/
if (filter)
return ocelot_vcap_filter_del(ocelot, filter);
return 0;
}
/* The reclassification rule must apply. See if it already exists
* or if it must be created.
*/
/* Treating as VLAN-untagged means using as classified VID equal to
* the bridge PVID, and PCP/DEI set to the port default QoS values.
*/
vid = pvid_vlan->vid;
val = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port);
pcp = ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X(val);
dei = !!(val & ANA_PORT_QOS_CFG_DP_DEFAULT_VAL);
if (filter) {
bool changed = false;
/* Filter exists, just update it */
if (filter->action.vid != vid) {
filter->action.vid = vid;
changed = true;
}
if (filter->action.pcp != pcp) {
filter->action.pcp = pcp;
changed = true;
}
if (filter->action.dei != dei) {
filter->action.dei = dei;
changed = true;
}
if (!changed)
return 0;
return ocelot_vcap_filter_replace(ocelot, filter);
}
/* Filter doesn't exist, create it */
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
if (!filter)
return -ENOMEM;
filter->key_type = OCELOT_VCAP_KEY_ANY;
filter->ingress_port_mask = BIT(port);
filter->vlan.tpid = OCELOT_VCAP_BIT_1;
filter->prio = 1;
filter->id.cookie = cookie;
filter->id.tc_offload = false;
filter->block_id = VCAP_IS1;
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
filter->lookup = 0;
filter->action.vid_replace_ena = true;
filter->action.pcp_dei_ena = true;
filter->action.vid = vid;
filter->action.pcp = pcp;
filter->action.dei = dei;
err = ocelot_vcap_filter_add(ocelot, filter, NULL);
if (err)
kfree(filter);
return err;
}
/* Default vlan to clasify for untagged frames (may be zero) */
static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
static int ocelot_port_set_pvid(struct ocelot *ocelot, int port,
const struct ocelot_bridge_vlan *pvid_vlan)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
......@@ -475,15 +624,23 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
* happens automatically), but also 802.1p traffic which gets
* classified to VLAN 0, but that is always in our RX filter, so it
* would get accepted were it not for this setting.
*
* Also, we only support the bridge 802.1Q VLAN protocol, so
* 802.1ad-tagged frames (carrying S-Tags) should be considered
* 802.1Q-untagged, and also dropped.
*/
if (!pvid_vlan && ocelot_port->vlan_aware)
val = ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA;
ocelot_rmw_gix(ocelot, val,
ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA,
ANA_PORT_DROP_CFG, port);
return ocelot_update_vlan_reclassify_rule(ocelot, port);
}
static struct ocelot_bridge_vlan *ocelot_bridge_vlan_find(struct ocelot *ocelot,
......@@ -631,7 +788,10 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
ANA_PORT_VLAN_CFG, port);
ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
err = ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
if (err)
return err;
ocelot_port_manage_port_tag(ocelot, port);
return 0;
......@@ -684,9 +844,12 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
return err;
/* Default ingress vlan classification */
if (pvid)
ocelot_port_set_pvid(ocelot, port,
if (pvid) {
err = ocelot_port_set_pvid(ocelot, port,
ocelot_bridge_vlan_find(ocelot, vid));
if (err)
return err;
}
/* Untagged egress vlan clasification */
ocelot_port_manage_port_tag(ocelot, port);
......@@ -712,8 +875,11 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
return err;
/* Ingress */
if (del_pvid)
ocelot_port_set_pvid(ocelot, port, NULL);
if (del_pvid) {
err = ocelot_port_set_pvid(ocelot, port, NULL);
if (err)
return err;
}
/* Egress */
ocelot_port_manage_port_tag(ocelot, port);
......@@ -1099,6 +1265,48 @@ void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
}
EXPORT_SYMBOL(ocelot_ptp_rx_timestamp);
void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp)
__acquires(&ocelot->inj_lock)
{
spin_lock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_inj_grp);
void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp)
__releases(&ocelot->inj_lock)
{
spin_unlock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_inj_grp);
void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp)
__acquires(&ocelot->inj_lock)
{
spin_lock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_xtr_grp);
void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp)
__releases(&ocelot->inj_lock)
{
spin_unlock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_xtr_grp);
void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp)
__acquires(&ocelot->xtr_lock)
{
spin_lock_bh(&ocelot->xtr_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_xtr_grp_bh);
void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp)
__releases(&ocelot->xtr_lock)
{
spin_unlock_bh(&ocelot->xtr_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_xtr_grp_bh);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
u64 timestamp, src_port, len;
......@@ -1109,6 +1317,8 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
u32 val, *buf;
int err;
lockdep_assert_held(&ocelot->xtr_lock);
err = ocelot_xtr_poll_xfh(ocelot, grp, xfh);
if (err)
return err;
......@@ -1184,6 +1394,8 @@ bool ocelot_can_inject(struct ocelot *ocelot, int grp)
{
u32 val = ocelot_read(ocelot, QS_INJ_STATUS);
lockdep_assert_held(&ocelot->inj_lock);
if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))))
return false;
if (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))
......@@ -1193,28 +1405,55 @@ bool ocelot_can_inject(struct ocelot *ocelot, int grp)
}
EXPORT_SYMBOL(ocelot_can_inject);
void ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag)
/**
* ocelot_ifh_set_basic - Set basic information in Injection Frame Header
* @ifh: Pointer to Injection Frame Header memory
* @ocelot: Switch private data structure
* @port: Egress port number
* @rew_op: Egress rewriter operation for PTP
* @skb: Pointer to socket buffer (packet)
*
* Populate the Injection Frame Header with basic information for this skb: the
* analyzer bypass bit, destination port, VLAN info, egress rewriter info.
*/
void ocelot_ifh_set_basic(void *ifh, struct ocelot *ocelot, int port,
u32 rew_op, struct sk_buff *skb)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct net_device *dev = skb->dev;
u64 vlan_tci, tag_type;
int qos_class;
ocelot_xmit_get_vlan_info(skb, ocelot_port->bridge, &vlan_tci,
&tag_type);
qos_class = netdev_get_num_tc(dev) ?
netdev_get_prio_tc_map(dev, skb->priority) : skb->priority;
memset(ifh, 0, OCELOT_TAG_LEN);
ocelot_ifh_set_bypass(ifh, 1);
ocelot_ifh_set_src(ifh, BIT_ULL(ocelot->num_phys_ports));
ocelot_ifh_set_dest(ifh, BIT_ULL(port));
ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C);
if (vlan_tag)
ocelot_ifh_set_vlan_tci(ifh, vlan_tag);
ocelot_ifh_set_qos_class(ifh, qos_class);
ocelot_ifh_set_tag_type(ifh, tag_type);
ocelot_ifh_set_vlan_tci(ifh, vlan_tci);
if (rew_op)
ocelot_ifh_set_rew_op(ifh, rew_op);
}
EXPORT_SYMBOL(ocelot_ifh_port_set);
EXPORT_SYMBOL(ocelot_ifh_set_basic);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb)
{
u32 ifh[OCELOT_TAG_LEN / 4] = {0};
u32 ifh[OCELOT_TAG_LEN / 4];
unsigned int i, count, last;
lockdep_assert_held(&ocelot->inj_lock);
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
ocelot_ifh_set_basic(ifh, ocelot, port, rew_op, skb);
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp);
......@@ -1247,6 +1486,8 @@ EXPORT_SYMBOL(ocelot_port_inject_frame);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{
lockdep_assert_held(&ocelot->xtr_lock);
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))
ocelot_read_rix(ocelot, QS_XTR_RD, grp);
}
......@@ -2532,7 +2773,7 @@ int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
ANA_PORT_QOS_CFG,
port);
return 0;
return ocelot_update_vlan_reclassify_rule(ocelot, port);
}
EXPORT_SYMBOL_GPL(ocelot_port_set_default_prio);
......@@ -2929,6 +3170,8 @@ int ocelot_init(struct ocelot *ocelot)
mutex_init(&ocelot->fwd_domain_lock);
spin_lock_init(&ocelot->ptp_clock_lock);
spin_lock_init(&ocelot->ts_id_lock);
spin_lock_init(&ocelot->inj_lock);
spin_lock_init(&ocelot->xtr_lock);
ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0);
if (!ocelot->owq)
......
......@@ -665,8 +665,7 @@ static int ocelot_fdma_prepare_skb(struct ocelot *ocelot, int port, u32 rew_op,
ifh = skb_push(skb, OCELOT_TAG_LEN);
skb_put(skb, ETH_FCS_LEN);
memset(ifh, 0, OCELOT_TAG_LEN);
ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
ocelot_ifh_set_basic(ifh, ocelot, port, rew_op, skb);
return 0;
}
......
......@@ -695,6 +695,7 @@ static void is1_entry_set(struct ocelot *ocelot, int ix,
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_MC, filter->dmac_mc);
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_BC, filter->dmac_bc);
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_VLAN_TAGGED, tag->tagged);
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TPID, tag->tpid);
vcap_key_set(vcap, &data, VCAP_IS1_HK_VID,
tag->vid.value, tag->vid.mask);
vcap_key_set(vcap, &data, VCAP_IS1_HK_PCP,
......
......@@ -51,6 +51,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
struct ocelot *ocelot = arg;
int grp = 0, err;
ocelot_lock_xtr_grp(ocelot, grp);
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
struct sk_buff *skb;
......@@ -69,6 +71,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
if (err < 0)
ocelot_drain_cpu_queue(ocelot, 0);
ocelot_unlock_xtr_grp(ocelot, grp);
return IRQ_HANDLED;
}
......
......@@ -5,6 +5,8 @@
#ifndef _NET_DSA_TAG_OCELOT_H
#define _NET_DSA_TAG_OCELOT_H
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/kthread.h>
#include <linux/packing.h>
#include <linux/skbuff.h>
......@@ -273,4 +275,49 @@ static inline u32 ocelot_ptp_rew_op(struct sk_buff *skb)
return rew_op;
}
/**
* ocelot_xmit_get_vlan_info: Determine VLAN_TCI and TAG_TYPE for injected frame
* @skb: Pointer to socket buffer
* @br: Pointer to bridge device that the port is under, if any
* @vlan_tci:
* @tag_type:
*
* If the port is under a VLAN-aware bridge, remove the VLAN header from the
* payload and move it into the DSA tag, which will make the switch classify
* the packet to the bridge VLAN. Otherwise, leave the classified VLAN at zero,
* which is the pvid of standalone ports (OCELOT_STANDALONE_PVID), although not
* of VLAN-unaware bridge ports (that would be ocelot_vlan_unaware_pvid()).
* Anyway, VID 0 is fine because it is stripped on egress for these port modes,
* and source address learning is not performed for packets injected from the
* CPU anyway, so it doesn't matter that the VID is "wrong".
*/
static inline void ocelot_xmit_get_vlan_info(struct sk_buff *skb,
struct net_device *br,
u64 *vlan_tci, u64 *tag_type)
{
struct vlan_ethhdr *hdr;
u16 proto, tci;
if (!br || !br_vlan_enabled(br)) {
*vlan_tci = 0;
*tag_type = IFH_TAG_TYPE_C;
return;
}
hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
br_vlan_get_proto(br, &proto);
if (ntohs(hdr->h_vlan_proto) == proto) {
vlan_remove_tag(skb, &tci);
*vlan_tci = tci;
} else {
rcu_read_lock();
br_vlan_get_pvid_rcu(br, &tci);
rcu_read_unlock();
*vlan_tci = tci;
}
*tag_type = (proto != ETH_P_8021Q) ? IFH_TAG_TYPE_S : IFH_TAG_TYPE_C;
}
#endif
......@@ -403,14 +403,18 @@ struct dsa_switch {
*/
u32 configure_vlan_while_not_filtering:1;
/* If the switch driver always programs the CPU port as egress tagged
* despite the VLAN configuration indicating otherwise, then setting
* @untag_bridge_pvid will force the DSA receive path to pop the
* bridge's default_pvid VLAN tagged frames to offer a consistent
* behavior between a vlan_filtering=0 and vlan_filtering=1 bridge
* device.
/* Pop the default_pvid of VLAN-unaware bridge ports from tagged frames.
* DEPRECATED: Do NOT set this field in new drivers. Instead look at
* the dsa_software_vlan_untag() comments.
*/
u32 untag_bridge_pvid:1;
/* Pop the default_pvid of VLAN-aware bridge ports from tagged frames.
* Useful if the switch cannot preserve the VLAN tag as seen on the
* wire for user port ingress, and chooses to send all frames as
* VLAN-tagged to the CPU, including those which were originally
* untagged.
*/
u32 untag_vlan_aware_bridge_pvid:1;
/* Let DSA manage the FDB entries towards the
* CPU, based on the software bridge database.
......
......@@ -813,6 +813,9 @@ struct ocelot {
const u32 *const *map;
struct list_head stats_regions;
spinlock_t inj_lock;
spinlock_t xtr_lock;
u32 pool_size[OCELOT_SB_NUM][OCELOT_SB_POOL_NUM];
int packet_buffer_size;
int num_frame_refs;
......@@ -966,10 +969,17 @@ void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target,
u32 val, u32 reg, u32 offset);
/* Packet I/O */
void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp);
void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp);
void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp);
void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp);
void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp);
void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp);
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb);
void ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag);
void ocelot_ifh_set_basic(void *ifh, struct ocelot *ocelot, int port,
u32 rew_op, struct sk_buff *skb);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp);
void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
......
......@@ -13,6 +13,7 @@
*/
#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream) ((upstream) << 16 | (port))
#define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port)
#define OCELOT_VCAP_IS1_VLAN_RECLASSIFY(ocelot, port) ((ocelot)->num_phys_ports + (port))
#define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port)
#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port))
#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2)
......@@ -499,6 +500,7 @@ struct ocelot_vcap_key_vlan {
struct ocelot_vcap_u8 pcp; /* PCP (3 bit) */
enum ocelot_vcap_bit dei; /* DEI */
enum ocelot_vcap_bit tagged; /* Tagged/untagged frame */
enum ocelot_vcap_bit tpid;
};
struct ocelot_vcap_key_etype {
......
......@@ -105,8 +105,9 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
p = netdev_priv(skb->dev);
if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
nskb = dsa_untag_bridge_pvid(skb);
if (unlikely(cpu_dp->ds->untag_bridge_pvid ||
cpu_dp->ds->untag_vlan_aware_bridge_pvid)) {
nskb = dsa_software_vlan_untag(skb);
if (!nskb) {
kfree_skb(skb);
return 0;
......
......@@ -44,46 +44,81 @@ static inline struct net_device *dsa_conduit_find_user(struct net_device *dev,
return NULL;
}
/* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged
* frames as untagged, since the bridge will not untag them.
/**
* dsa_software_untag_vlan_aware_bridge: Software untagging for VLAN-aware bridge
* @skb: Pointer to received socket buffer (packet)
* @br: Pointer to bridge upper interface of ingress port
* @vid: Parsed VID from packet
*
* The bridge can process tagged packets. Software like STP/PTP may not. The
* bridge can also process untagged packets, to the same effect as if they were
* tagged with the PVID of the ingress port. So packets tagged with the PVID of
* the bridge port must be software-untagged, to support both use cases.
*/
static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
static inline void dsa_software_untag_vlan_aware_bridge(struct sk_buff *skb,
struct net_device *br,
u16 vid)
{
struct dsa_port *dp = dsa_user_to_port(skb->dev);
struct net_device *br = dsa_port_bridge_dev_get(dp);
struct net_device *dev = skb->dev;
struct net_device *upper_dev;
u16 vid, pvid, proto;
u16 pvid, proto;
int err;
if (!br || br_vlan_enabled(br))
return skb;
err = br_vlan_get_proto(br, &proto);
if (err)
return skb;
return;
/* Move VLAN tag from data to hwaccel */
if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) {
skb = skb_vlan_untag(skb);
if (!skb)
return NULL;
}
if (!skb_vlan_tag_present(skb))
return skb;
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
if (err)
return;
vid = skb_vlan_tag_get_id(skb);
if (vid == pvid && skb->vlan_proto == htons(proto))
__vlan_hwaccel_clear_tag(skb);
}
/* We already run under an RCU read-side critical section since
* we are called from netif_receive_skb_list_internal().
/**
* dsa_software_untag_vlan_unaware_bridge: Software untagging for VLAN-unaware bridge
* @skb: Pointer to received socket buffer (packet)
* @br: Pointer to bridge upper interface of ingress port
* @vid: Parsed VID from packet
*
* The bridge ignores all VLAN tags. Software like STP/PTP may not (it may run
* on the plain port, or on a VLAN upper interface). Maybe packets are coming
* to software as tagged with a driver-defined VID which is NOT equal to the
* PVID of the bridge port (since the bridge is VLAN-unaware, its configuration
* should NOT be committed to hardware). DSA needs a method for this private
* VID to be communicated by software to it, and if packets are tagged with it,
* software-untag them. Note: the private VID may be different per bridge, to
* support the FDB isolation use case.
*
* FIXME: this is currently implemented based on the broken assumption that
* the "private VID" used by the driver in VLAN-unaware mode is equal to the
* bridge PVID. It should not be, except for a coincidence; the bridge PVID is
* irrelevant to the data path in the VLAN-unaware mode. Thus, the VID that
* this function removes is wrong.
*
* All users of ds->untag_bridge_pvid should fix their drivers, if necessary,
* to make the two independent. Only then, if there still remains a need to
* strip the private VID from packets, then a new ds->ops->get_private_vid()
* API shall be introduced to communicate to DSA what this VID is, which needs
* to be stripped here.
*/
err = br_vlan_get_pvid_rcu(dev, &pvid);
static inline void dsa_software_untag_vlan_unaware_bridge(struct sk_buff *skb,
struct net_device *br,
u16 vid)
{
struct net_device *upper_dev;
u16 pvid, proto;
int err;
err = br_vlan_get_proto(br, &proto);
if (err)
return skb;
return;
if (vid != pvid)
return skb;
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
if (err)
return;
if (vid != pvid || skb->vlan_proto != htons(proto))
return;
/* The sad part about attempting to untag from DSA is that we
* don't know, unless we check, if the skb will end up in
......@@ -95,10 +130,50 @@ static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
* definitely keep the tag, to make sure it keeps working.
*/
upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid);
if (upper_dev)
if (!upper_dev)
__vlan_hwaccel_clear_tag(skb);
}
/**
* dsa_software_vlan_untag: Software VLAN untagging in DSA receive path
* @skb: Pointer to socket buffer (packet)
*
* Receive path method for switches which cannot avoid tagging all packets
* towards the CPU port. Called when ds->untag_bridge_pvid (legacy) or
* ds->untag_vlan_aware_bridge_pvid is set to true.
*
* As a side effect of this method, any VLAN tag from the skb head is moved
* to hwaccel.
*/
static inline struct sk_buff *dsa_software_vlan_untag(struct sk_buff *skb)
{
struct dsa_port *dp = dsa_user_to_port(skb->dev);
struct net_device *br = dsa_port_bridge_dev_get(dp);
u16 vid;
/* software untagging for standalone ports not yet necessary */
if (!br)
return skb;
__vlan_hwaccel_clear_tag(skb);
/* Move VLAN tag from data to hwaccel */
if (!skb_vlan_tag_present(skb)) {
skb = skb_vlan_untag(skb);
if (!skb)
return NULL;
}
if (!skb_vlan_tag_present(skb))
return skb;
vid = skb_vlan_tag_get_id(skb);
if (br_vlan_enabled(br)) {
if (dp->ds->untag_vlan_aware_bridge_pvid)
dsa_software_untag_vlan_aware_bridge(skb, br, vid);
} else {
if (dp->ds->untag_bridge_pvid)
dsa_software_untag_vlan_unaware_bridge(skb, br, vid);
}
return skb;
}
......
......@@ -8,40 +8,6 @@
#define OCELOT_NAME "ocelot"
#define SEVILLE_NAME "seville"
/* If the port is under a VLAN-aware bridge, remove the VLAN header from the
* payload and move it into the DSA tag, which will make the switch classify
* the packet to the bridge VLAN. Otherwise, leave the classified VLAN at zero,
* which is the pvid of standalone and VLAN-unaware bridge ports.
*/
static void ocelot_xmit_get_vlan_info(struct sk_buff *skb, struct dsa_port *dp,
u64 *vlan_tci, u64 *tag_type)
{
struct net_device *br = dsa_port_bridge_dev_get(dp);
struct vlan_ethhdr *hdr;
u16 proto, tci;
if (!br || !br_vlan_enabled(br)) {
*vlan_tci = 0;
*tag_type = IFH_TAG_TYPE_C;
return;
}
hdr = skb_vlan_eth_hdr(skb);
br_vlan_get_proto(br, &proto);
if (ntohs(hdr->h_vlan_proto) == proto) {
vlan_remove_tag(skb, &tci);
*vlan_tci = tci;
} else {
rcu_read_lock();
br_vlan_get_pvid_rcu(br, &tci);
rcu_read_unlock();
*vlan_tci = tci;
}
*tag_type = (proto != ETH_P_8021Q) ? IFH_TAG_TYPE_S : IFH_TAG_TYPE_C;
}
static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
__be32 ifh_prefix, void **ifh)
{
......@@ -53,7 +19,8 @@ static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
u32 rew_op = 0;
u64 qos_class;
ocelot_xmit_get_vlan_info(skb, dp, &vlan_tci, &tag_type);
ocelot_xmit_get_vlan_info(skb, dsa_port_bridge_dev_get(dp), &vlan_tci,
&tag_type);
qos_class = netdev_get_num_tc(netdev) ?
netdev_get_prio_tc_map(netdev, skb->priority) : skb->priority;
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="ping_ipv4 ping_ipv6 learning flooding vlan_deletion extern_learn"
ALL_TESTS="ping_ipv4 ping_ipv6 learning flooding vlan_deletion extern_learn other_tpid"
NUM_NETIFS=4
CHECK_TC="yes"
source lib.sh
......@@ -142,6 +142,58 @@ extern_learn()
bridge fdb del de:ad:be:ef:13:37 dev $swp1 master vlan 1 &> /dev/null
}
other_tpid()
{
local mac=de:ad:be:ef:13:37
# Test that packets with TPID 802.1ad VID 3 + TPID 802.1Q VID 5 are
# classified as untagged by a bridge with vlan_protocol 802.1Q, and
# are processed in the PVID of the ingress port (here 1). Not VID 3,
# and not VID 5.
RET=0
tc qdisc add dev $h2 clsact
tc filter add dev $h2 ingress protocol all pref 1 handle 101 \
flower dst_mac $mac action drop
ip link set $h2 promisc on
ethtool -K $h2 rx-vlan-filter off rx-vlan-stag-filter off
$MZ -q $h1 -c 1 -b $mac -a own "88:a8 00:03 81:00 00:05 08:00 aa-aa-aa-aa-aa-aa-aa-aa-aa"
sleep 1
# Match on 'self' addresses as well, for those drivers which
# do not push their learned addresses to the bridge software
# database
bridge -j fdb show $swp1 | \
jq -e ".[] | select(.mac == \"$(mac_get $h1)\") | select(.vlan == 1)" &> /dev/null
check_err $? "FDB entry was not learned when it should"
log_test "FDB entry in PVID for VLAN-tagged with other TPID"
RET=0
tc -j -s filter show dev $h2 ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
check_err $? "Packet was not forwarded when it should"
log_test "Reception of VLAN with other TPID as untagged"
bridge vlan del dev $swp1 vid 1
$MZ -q $h1 -c 1 -b $mac -a own "88:a8 00:03 81:00 00:05 08:00 aa-aa-aa-aa-aa-aa-aa-aa-aa"
sleep 1
RET=0
tc -j -s filter show dev $h2 ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
check_err $? "Packet was forwarded when should not"
log_test "Reception of VLAN with other TPID as untagged (no PVID)"
bridge vlan add dev $swp1 vid 1 pvid untagged
ip link set $h2 promisc off
tc qdisc del dev $h2 clsact
}
trap cleanup EXIT
setup_prepare
......
......@@ -500,6 +500,11 @@ check_err_fail()
fi
}
xfail()
{
FAIL_TO_XFAIL=yes "$@"
}
xfail_on_slow()
{
if [[ $KSFT_MACHINE_SLOW = yes ]]; then
......@@ -1113,6 +1118,39 @@ mac_get()
ip -j link show dev $if_name | jq -r '.[]["address"]'
}
ether_addr_to_u64()
{
local addr="$1"
local order="$((1 << 40))"
local val=0
local byte
addr="${addr//:/ }"
for byte in $addr; do
byte="0x$byte"
val=$((val + order * byte))
order=$((order >> 8))
done
printf "0x%x" $val
}
u64_to_ether_addr()
{
local val=$1
local byte
local i
for ((i = 40; i >= 0; i -= 8)); do
byte=$(((val & (0xff << i)) >> i))
printf "%02x" $byte
if [ $i -ne 0 ]; then
printf ":"
fi
done
}
ipv6_lladdr_get()
{
local if_name=$1
......@@ -2229,3 +2267,22 @@ absval()
echo $((v > 0 ? v : -v))
}
has_unicast_flt()
{
local dev=$1; shift
local mac_addr=$(mac_get $dev)
local tmp=$(ether_addr_to_u64 $mac_addr)
local promisc
ip link set $dev up
ip link add link $dev name macvlan-tmp type macvlan mode private
ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))
ip link set macvlan-tmp up
promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')
ip link del macvlan-tmp
[[ $promisc == 1 ]] && echo "no" || echo "yes"
}
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="standalone bridge"
ALL_TESTS="standalone vlan_unaware_bridge vlan_aware_bridge test_vlan \
vlan_over_vlan_unaware_bridged_port vlan_over_vlan_aware_bridged_port \
vlan_over_vlan_unaware_bridge vlan_over_vlan_aware_bridge"
NUM_NETIFS=2
PING_COUNT=1
REQUIRE_MTOOLS=yes
......@@ -37,9 +39,68 @@ UNKNOWN_MACV6_MC_ADDR1="33:33:01:02:03:05"
UNKNOWN_MACV6_MC_ADDR2="33:33:01:02:03:06"
UNKNOWN_MACV6_MC_ADDR3="33:33:01:02:03:07"
NON_IP_MC="01:02:03:04:05:06"
NON_IP_PKT="00:04 48:45:4c:4f"
BC="ff:ff:ff:ff:ff:ff"
PTP_1588_L2_SYNC=" \
01:1b:19:00:00:00 00:00:de:ad:be:ef 88:f7 00 02 \
00 2c 00 00 02 00 00 00 00 00 00 00 00 00 00 00 \
00 00 3e 37 63 ff fe cf 17 0e 00 01 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00"
PTP_1588_L2_FOLLOW_UP=" \
01:1b:19:00:00:00 00:00:de:ad:be:ef 88:f7 08 02 \
00 2c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 3e 37 63 ff fe cf 17 0e 00 01 00 00 02 00 \
00 00 66 83 c5 f1 17 97 ed f0"
PTP_1588_L2_PDELAY_REQ=" \
01:80:c2:00:00:0e 00:00:de:ad:be:ef 88:f7 02 02 \
00 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 3e 37 63 ff fe cf 17 0e 00 01 00 06 05 7f \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 00 00"
PTP_1588_IPV4_SYNC=" \
01:00:5e:00:01:81 00:00:de:ad:be:ef 08:00 45 00 \
00 48 0a 9a 40 00 01 11 cb 88 c0 00 02 01 e0 00 \
01 81 01 3f 01 3f 00 34 a3 c8 00 02 00 2c 00 00 \
02 00 00 00 00 00 00 00 00 00 00 00 00 00 3e 37 \
63 ff fe cf 17 0e 00 01 00 00 00 00 00 00 00 00 \
00 00 00 00 00 00"
PTP_1588_IPV4_FOLLOW_UP="
01:00:5e:00:01:81 00:00:de:ad:be:ef 08:00 45 00 \
00 48 0a 9b 40 00 01 11 cb 87 c0 00 02 01 e0 00 \
01 81 01 40 01 40 00 34 a3 c8 08 02 00 2c 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 3e 37 \
63 ff fe cf 17 0e 00 01 00 00 02 00 00 00 66 83 \
c6 0f 1d 9a 61 87"
PTP_1588_IPV4_PDELAY_REQ=" \
01:00:5e:00:00:6b 00:00:de:ad:be:ef 08:00 45 00 \
00 52 35 a9 40 00 01 11 a1 85 c0 00 02 01 e0 00 \
00 6b 01 3f 01 3f 00 3e a2 bc 02 02 00 36 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 3e 37 \
63 ff fe cf 17 0e 00 01 00 01 05 7f 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
PTP_1588_IPV6_SYNC=" \
33:33:00:00:01:81 00:00:de:ad:be:ef 86:dd 60 06 \
7c 2f 00 36 11 01 20 01 0d b8 00 01 00 00 00 00 \
00 00 00 00 00 01 ff 0e 00 00 00 00 00 00 00 00 \
00 00 00 00 01 81 01 3f 01 3f 00 36 2e 92 00 02 \
00 2c 00 00 02 00 00 00 00 00 00 00 00 00 00 00 \
00 00 3e 37 63 ff fe cf 17 0e 00 01 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00"
PTP_1588_IPV6_FOLLOW_UP=" \
33:33:00:00:01:81 00:00:de:ad:be:ef 86:dd 60 0a \
00 bc 00 36 11 01 20 01 0d b8 00 01 00 00 00 00 \
00 00 00 00 00 01 ff 0e 00 00 00 00 00 00 00 00 \
00 00 00 00 01 81 01 40 01 40 00 36 2e 92 08 02 \
00 2c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 3e 37 63 ff fe cf 17 0e 00 01 00 00 02 00 \
00 00 66 83 c6 2a 32 09 bd 74 00 00"
PTP_1588_IPV6_PDELAY_REQ=" \
33:33:00:00:00:6b 00:00:de:ad:be:ef 86:dd 60 0c \
5c fd 00 40 11 01 fe 80 00 00 00 00 00 00 3c 37 \
63 ff fe cf 17 0e ff 02 00 00 00 00 00 00 00 00 \
00 00 00 00 00 6b 01 3f 01 3f 00 40 b4 54 02 02 \
00 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 3e 37 63 ff fe cf 17 0e 00 01 00 01 05 7f \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 00 00 00 00"
# Disable promisc to ensure we don't receive unknown MAC DA packets
export TCPDUMP_EXTRA_FLAGS="-pl"
......@@ -47,13 +108,15 @@ export TCPDUMP_EXTRA_FLAGS="-pl"
h1=${NETIFS[p1]}
h2=${NETIFS[p2]}
send_non_ip()
send_raw()
{
local if_name=$1
local smac=$2
local dmac=$3
local if_name=$1; shift
local pkt="$1"; shift
local smac=$(mac_get $if_name)
pkt="${pkt/00:00:de:ad:be:ef/$smac}"
$MZ -q $if_name "$dmac $smac $NON_IP_PKT"
$MZ -q $if_name "$pkt"
}
send_uc_ipv4()
......@@ -68,10 +131,11 @@ send_uc_ipv4()
check_rcv()
{
local if_name=$1
local type=$2
local pattern=$3
local should_receive=$4
local if_name=$1; shift
local type=$1; shift
local pattern=$1; shift
local should_receive=$1; shift
local test_name="$1"; shift
local should_fail=
[ $should_receive = true ] && should_fail=0 || should_fail=1
......@@ -81,7 +145,7 @@ check_rcv()
check_err_fail "$should_fail" "$?" "reception"
log_test "$if_name: $type"
log_test "$test_name: $type"
}
mc_route_prepare()
......@@ -104,44 +168,78 @@ mc_route_destroy()
run_test()
{
local rcv_if_name=$1
local smac=$(mac_get $h1)
local send_if_name=$1; shift
local rcv_if_name=$1; shift
local skip_ptp=$1; shift
local no_unicast_flt=$1; shift
local test_name="$1"; shift
local smac=$(mac_get $send_if_name)
local rcv_dmac=$(mac_get $rcv_if_name)
local should_receive
tcpdump_start $rcv_if_name
mc_route_prepare $h1
mc_route_prepare $send_if_name
mc_route_prepare $rcv_if_name
send_uc_ipv4 $h1 $rcv_dmac
send_uc_ipv4 $h1 $MACVLAN_ADDR
send_uc_ipv4 $h1 $UNKNOWN_UC_ADDR1
send_uc_ipv4 $send_if_name $rcv_dmac
send_uc_ipv4 $send_if_name $MACVLAN_ADDR
send_uc_ipv4 $send_if_name $UNKNOWN_UC_ADDR1
ip link set dev $rcv_if_name promisc on
send_uc_ipv4 $h1 $UNKNOWN_UC_ADDR2
mc_send $h1 $UNKNOWN_IPV4_MC_ADDR2
mc_send $h1 $UNKNOWN_IPV6_MC_ADDR2
send_uc_ipv4 $send_if_name $UNKNOWN_UC_ADDR2
mc_send $send_if_name $UNKNOWN_IPV4_MC_ADDR2
mc_send $send_if_name $UNKNOWN_IPV6_MC_ADDR2
ip link set dev $rcv_if_name promisc off
mc_join $rcv_if_name $JOINED_IPV4_MC_ADDR
mc_send $h1 $JOINED_IPV4_MC_ADDR
mc_send $send_if_name $JOINED_IPV4_MC_ADDR
mc_leave
mc_join $rcv_if_name $JOINED_IPV6_MC_ADDR
mc_send $h1 $JOINED_IPV6_MC_ADDR
mc_send $send_if_name $JOINED_IPV6_MC_ADDR
mc_leave
mc_send $h1 $UNKNOWN_IPV4_MC_ADDR1
mc_send $h1 $UNKNOWN_IPV6_MC_ADDR1
mc_send $send_if_name $UNKNOWN_IPV4_MC_ADDR1
mc_send $send_if_name $UNKNOWN_IPV6_MC_ADDR1
ip link set dev $rcv_if_name allmulticast on
send_uc_ipv4 $h1 $UNKNOWN_UC_ADDR3
mc_send $h1 $UNKNOWN_IPV4_MC_ADDR3
mc_send $h1 $UNKNOWN_IPV6_MC_ADDR3
send_uc_ipv4 $send_if_name $UNKNOWN_UC_ADDR3
mc_send $send_if_name $UNKNOWN_IPV4_MC_ADDR3
mc_send $send_if_name $UNKNOWN_IPV6_MC_ADDR3
ip link set dev $rcv_if_name allmulticast off
mc_route_destroy $rcv_if_name
mc_route_destroy $h1
mc_route_destroy $send_if_name
if [ $skip_ptp = false ]; then
ip maddress add 01:1b:19:00:00:00 dev $rcv_if_name
send_raw $send_if_name "$PTP_1588_L2_SYNC"
send_raw $send_if_name "$PTP_1588_L2_FOLLOW_UP"
ip maddress del 01:1b:19:00:00:00 dev $rcv_if_name
ip maddress add 01:80:c2:00:00:0e dev $rcv_if_name
send_raw $send_if_name "$PTP_1588_L2_PDELAY_REQ"
ip maddress del 01:80:c2:00:00:0e dev $rcv_if_name
mc_join $rcv_if_name 224.0.1.129
send_raw $send_if_name "$PTP_1588_IPV4_SYNC"
send_raw $send_if_name "$PTP_1588_IPV4_FOLLOW_UP"
mc_leave
mc_join $rcv_if_name 224.0.0.107
send_raw $send_if_name "$PTP_1588_IPV4_PDELAY_REQ"
mc_leave
mc_join $rcv_if_name ff0e::181
send_raw $send_if_name "$PTP_1588_IPV6_SYNC"
send_raw $send_if_name "$PTP_1588_IPV6_FOLLOW_UP"
mc_leave
mc_join $rcv_if_name ff02::6b
send_raw $send_if_name "$PTP_1588_IPV6_PDELAY_REQ"
mc_leave
fi
sleep 1
......@@ -149,61 +247,99 @@ run_test()
check_rcv $rcv_if_name "Unicast IPv4 to primary MAC address" \
"$smac > $rcv_dmac, ethertype IPv4 (0x0800)" \
true
true "$test_name"
check_rcv $rcv_if_name "Unicast IPv4 to macvlan MAC address" \
"$smac > $MACVLAN_ADDR, ethertype IPv4 (0x0800)" \
true
true "$test_name"
xfail_on_veth $h1 \
[ $no_unicast_flt = true ] && should_receive=true || should_receive=false
check_rcv $rcv_if_name "Unicast IPv4 to unknown MAC address" \
"$smac > $UNKNOWN_UC_ADDR1, ethertype IPv4 (0x0800)" \
false
$should_receive "$test_name"
check_rcv $rcv_if_name "Unicast IPv4 to unknown MAC address, promisc" \
"$smac > $UNKNOWN_UC_ADDR2, ethertype IPv4 (0x0800)" \
true
true "$test_name"
xfail_on_veth $h1 \
[ $no_unicast_flt = true ] && should_receive=true || should_receive=false
check_rcv $rcv_if_name \
"Unicast IPv4 to unknown MAC address, allmulti" \
"$smac > $UNKNOWN_UC_ADDR3, ethertype IPv4 (0x0800)" \
false
$should_receive "$test_name"
check_rcv $rcv_if_name "Multicast IPv4 to joined group" \
"$smac > $JOINED_MACV4_MC_ADDR, ethertype IPv4 (0x0800)" \
true
true "$test_name"
xfail_on_veth $h1 \
xfail \
check_rcv $rcv_if_name \
"Multicast IPv4 to unknown group" \
"$smac > $UNKNOWN_MACV4_MC_ADDR1, ethertype IPv4 (0x0800)" \
false
false "$test_name"
check_rcv $rcv_if_name "Multicast IPv4 to unknown group, promisc" \
"$smac > $UNKNOWN_MACV4_MC_ADDR2, ethertype IPv4 (0x0800)" \
true
true "$test_name"
check_rcv $rcv_if_name "Multicast IPv4 to unknown group, allmulti" \
"$smac > $UNKNOWN_MACV4_MC_ADDR3, ethertype IPv4 (0x0800)" \
true
true "$test_name"
check_rcv $rcv_if_name "Multicast IPv6 to joined group" \
"$smac > $JOINED_MACV6_MC_ADDR, ethertype IPv6 (0x86dd)" \
true
true "$test_name"
xfail_on_veth $h1 \
xfail \
check_rcv $rcv_if_name "Multicast IPv6 to unknown group" \
"$smac > $UNKNOWN_MACV6_MC_ADDR1, ethertype IPv6 (0x86dd)" \
false
false "$test_name"
check_rcv $rcv_if_name "Multicast IPv6 to unknown group, promisc" \
"$smac > $UNKNOWN_MACV6_MC_ADDR2, ethertype IPv6 (0x86dd)" \
true
true "$test_name"
check_rcv $rcv_if_name "Multicast IPv6 to unknown group, allmulti" \
"$smac > $UNKNOWN_MACV6_MC_ADDR3, ethertype IPv6 (0x86dd)" \
true
true "$test_name"
if [ $skip_ptp = false ]; then
check_rcv $rcv_if_name "1588v2 over L2 transport, Sync" \
"ethertype PTP (0x88f7).* PTPv2.* msg type : sync msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over L2 transport, Follow-Up" \
"ethertype PTP (0x88f7).* PTPv2.* msg type : follow up msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over L2 transport, Peer Delay Request" \
"ethertype PTP (0x88f7).* PTPv2.* msg type : peer delay req msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over IPv4, Sync" \
"ethertype IPv4 (0x0800).* PTPv2.* msg type : sync msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over IPv4, Follow-Up" \
"ethertype IPv4 (0x0800).* PTPv2.* msg type : follow up msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over IPv4, Peer Delay Request" \
"ethertype IPv4 (0x0800).* PTPv2.* msg type : peer delay req msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over IPv6, Sync" \
"ethertype IPv6 (0x86dd).* PTPv2.* msg type : sync msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over IPv6, Follow-Up" \
"ethertype IPv6 (0x86dd).* PTPv2.* msg type : follow up msg" \
true "$test_name"
check_rcv $rcv_if_name "1588v2 over IPv6, Peer Delay Request" \
"ethertype IPv6 (0x86dd).* PTPv2.* msg type : peer delay req msg" \
true "$test_name"
fi
tcpdump_cleanup $rcv_if_name
}
......@@ -228,57 +364,208 @@ h2_destroy()
simple_if_fini $h2 $H2_IPV4/24 $H2_IPV6/64
}
h1_vlan_create()
{
simple_if_init $h1
vlan_create $h1 100 v$h1 $H1_IPV4/24 $H1_IPV6/64
}
h1_vlan_destroy()
{
vlan_destroy $h1 100
simple_if_fini $h1
}
h2_vlan_create()
{
simple_if_init $h2
vlan_create $h2 100 v$h2 $H2_IPV4/24 $H2_IPV6/64
}
h2_vlan_destroy()
{
vlan_destroy $h2 100
simple_if_fini $h2
}
bridge_create()
{
ip link add br0 type bridge
local vlan_filtering=$1
ip link add br0 type bridge vlan_filtering $vlan_filtering
ip link set br0 address $BRIDGE_ADDR
ip link set br0 up
ip link set $h2 master br0
ip link set $h2 up
simple_if_init br0 $H2_IPV4/24 $H2_IPV6/64
}
bridge_destroy()
{
simple_if_fini br0 $H2_IPV4/24 $H2_IPV6/64
ip link del br0
}
standalone()
macvlan_create()
{
h1_create
h2_create
local lower=$1
ip link add link $h2 name macvlan0 type macvlan mode private
ip link add link $lower name macvlan0 type macvlan mode private
ip link set macvlan0 address $MACVLAN_ADDR
ip link set macvlan0 up
}
run_test $h2
macvlan_destroy()
{
ip link del macvlan0
}
standalone()
{
local no_unicast_flt=true
local skip_ptp=false
if [ $(has_unicast_flt $h2) = yes ]; then
no_unicast_flt=false
fi
h1_create
h2_create
macvlan_create $h2
run_test $h1 $h2 $skip_ptp $no_unicast_flt "$h2"
macvlan_destroy
h2_destroy
h1_destroy
}
bridge()
test_bridge()
{
local no_unicast_flt=true
local vlan_filtering=$1
local skip_ptp=true
h1_create
bridge_create
bridge_create $vlan_filtering
simple_if_init br0 $H2_IPV4/24 $H2_IPV6/64
macvlan_create br0
ip link add link br0 name macvlan0 type macvlan mode private
ip link set macvlan0 address $MACVLAN_ADDR
ip link set macvlan0 up
run_test $h1 br0 $skip_ptp $no_unicast_flt \
"vlan_filtering=$vlan_filtering bridge"
run_test br0
macvlan_destroy
simple_if_fini br0 $H2_IPV4/24 $H2_IPV6/64
bridge_destroy
h1_destroy
}
ip link del macvlan0
vlan_unaware_bridge()
{
test_bridge 0
}
vlan_aware_bridge()
{
test_bridge 1
}
test_vlan()
{
local no_unicast_flt=true
local skip_ptp=false
if [ $(has_unicast_flt $h2) = yes ]; then
no_unicast_flt=false
fi
h1_vlan_create
h2_vlan_create
macvlan_create $h2.100
run_test $h1.100 $h2.100 $skip_ptp $no_unicast_flt "VLAN upper"
macvlan_destroy
h2_vlan_destroy
h1_vlan_destroy
}
vlan_over_bridged_port()
{
local no_unicast_flt=true
local vlan_filtering=$1
local skip_ptp=false
# br_manage_promisc() will not force a single vlan_filtering port to
# promiscuous mode, so we should still expect unicast filtering to take
# place if the device can do it.
if [ $(has_unicast_flt $h2) = yes ] && [ $vlan_filtering = 1 ]; then
no_unicast_flt=false
fi
h1_vlan_create
h2_vlan_create
bridge_create $vlan_filtering
macvlan_create $h2.100
run_test $h1.100 $h2.100 $skip_ptp $no_unicast_flt \
"VLAN over vlan_filtering=$vlan_filtering bridged port"
macvlan_destroy
bridge_destroy
h1_destroy
h2_vlan_destroy
h1_vlan_destroy
}
vlan_over_vlan_unaware_bridged_port()
{
vlan_over_bridged_port 0
}
vlan_over_vlan_aware_bridged_port()
{
vlan_over_bridged_port 1
}
vlan_over_bridge()
{
local no_unicast_flt=true
local vlan_filtering=$1
local skip_ptp=true
h1_vlan_create
bridge_create $vlan_filtering
simple_if_init br0
vlan_create br0 100 vbr0 $H2_IPV4/24 $H2_IPV6/64
macvlan_create br0.100
if [ $vlan_filtering = 1 ]; then
bridge vlan add dev $h2 vid 100 master
bridge vlan add dev br0 vid 100 self
fi
run_test $h1.100 br0.100 $skip_ptp $no_unicast_flt \
"VLAN over vlan_filtering=$vlan_filtering bridge"
if [ $vlan_filtering = 1 ]; then
bridge vlan del dev br0 vid 100 self
bridge vlan del dev $h2 vid 100 master
fi
macvlan_destroy
vlan_destroy br0 100
simple_if_fini br0
bridge_destroy
h1_vlan_destroy
}
vlan_over_vlan_unaware_bridge()
{
vlan_over_bridge 0
}
vlan_over_vlan_aware_bridge()
{
vlan_over_bridge 1
}
cleanup()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment