mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
3d93a1448e
|
|
@ -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,11 +324,41 @@ 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,
|
||||
const struct ocelot_vcap_filter *trap)
|
||||
{
|
||||
|
|
@ -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,9 +453,158 @@ 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,
|
||||
const struct ocelot_bridge_vlan *pvid_vlan)
|
||||
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];
|
||||
u16 pvid = ocelot_vlan_unaware_pvid(ocelot, ocelot_port->bridge);
|
||||
|
|
@ -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,
|
||||
ocelot_bridge_vlan_find(ocelot, vid));
|
||||
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;
|
||||
|
|
|
|||
141
net/dsa/tag.h
141
net/dsa/tag.h
|
|
@ -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;
|
||||
|
||||
vid = skb_vlan_tag_get_id(skb);
|
||||
|
||||
/* We already run under an RCU read-side critical section since
|
||||
* we are called from netif_receive_skb_list_internal().
|
||||
*/
|
||||
err = br_vlan_get_pvid_rcu(dev, &pvid);
|
||||
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
|
||||
if (err)
|
||||
return skb;
|
||||
return;
|
||||
|
||||
if (vid != pvid)
|
||||
return skb;
|
||||
if (vid == pvid && skb->vlan_proto == htons(proto))
|
||||
__vlan_hwaccel_clear_tag(skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#!/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)
|
||||
|
||||
$MZ -q $if_name "$dmac $smac $NON_IP_PKT"
|
||||
pkt="${pkt/00:00:de:ad:be:ef/$smac}"
|
||||
|
||||
$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 \
|
||||
check_rcv $rcv_if_name "Unicast IPv4 to unknown MAC address" \
|
||||
"$smac > $UNKNOWN_UC_ADDR1, ethertype IPv4 (0x0800)" \
|
||||
false
|
||||
[ $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)" \
|
||||
$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 \
|
||||
check_rcv $rcv_if_name \
|
||||
"Unicast IPv4 to unknown MAC address, allmulti" \
|
||||
"$smac > $UNKNOWN_UC_ADDR3, ethertype IPv4 (0x0800)" \
|
||||
false
|
||||
[ $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)" \
|
||||
$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,59 +364,210 @@ 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
|
||||
}
|
||||
|
||||
macvlan_create()
|
||||
{
|
||||
local lower=$1
|
||||
|
||||
ip link add link $lower name macvlan0 type macvlan mode private
|
||||
ip link set macvlan0 address $MACVLAN_ADDR
|
||||
ip link set macvlan0 up
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
ip link add link $h2 name macvlan0 type macvlan mode private
|
||||
ip link set macvlan0 address $MACVLAN_ADDR
|
||||
ip link set macvlan0 up
|
||||
|
||||
run_test $h2
|
||||
|
||||
ip link del macvlan0
|
||||
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 br0
|
||||
|
||||
ip link del macvlan0
|
||||
run_test $h1 br0 $skip_ptp $no_unicast_flt \
|
||||
"vlan_filtering=$vlan_filtering bridge"
|
||||
|
||||
macvlan_destroy
|
||||
simple_if_fini br0 $H2_IPV4/24 $H2_IPV6/64
|
||||
bridge_destroy
|
||||
h1_destroy
|
||||
}
|
||||
|
||||
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
|
||||
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()
|
||||
{
|
||||
pre_cleanup
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user