+ unixctl_command_register(
+ "ofproto/trace",
+ "bridge {tun_id in_port packet | odp_flow [-generate]}",
+ 2, 5, ofproto_unixctl_trace, NULL);
+ unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
+ ofproto_unixctl_fdb_flush, NULL);
+ unixctl_command_register("fdb/show", "bridge", 1, 1,
+ ofproto_unixctl_fdb_show, NULL);
+ unixctl_command_register("ofproto/clog", "", 0, 0,
+ ofproto_dpif_clog, NULL);
+ unixctl_command_register("ofproto/unclog", "", 0, 0,
+ ofproto_dpif_unclog, NULL);
+ unixctl_command_register("ofproto/self-check", "[bridge]", 0, 1,
+ ofproto_dpif_self_check, NULL);
+}
+\f
+/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
+ *
+ * This is deprecated. It is only for compatibility with broken device drivers
+ * in old versions of Linux that do not properly support VLANs when VLAN
+ * devices are not used. When broken device drivers are no longer in
+ * widespread use, we will delete these interfaces. */
+
+static int
+set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto);
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ if (realdev_ofp_port == ofport->realdev_ofp_port
+ && vid == ofport->vlandev_vid) {
+ return 0;
+ }
+
+ ofproto->need_revalidate = true;
+
+ if (ofport->realdev_ofp_port) {
+ vsp_remove(ofport);
+ }
+ if (realdev_ofp_port && ofport->bundle) {
+ /* vlandevs are enslaved to their realdevs, so they are not allowed to
+ * themselves be part of a bundle. */
+ bundle_set(ofport->up.ofproto, ofport->bundle, NULL);
+ }
+
+ ofport->realdev_ofp_port = realdev_ofp_port;
+ ofport->vlandev_vid = vid;
+
+ if (realdev_ofp_port) {
+ vsp_add(ofport, realdev_ofp_port, vid);
+ }
+
+ return 0;
+}
+
+static uint32_t
+hash_realdev_vid(uint16_t realdev_ofp_port, int vid)
+{
+ return hash_2words(realdev_ofp_port, vid);
+}
+
+static uint32_t
+vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto,
+ uint32_t realdev_odp_port, ovs_be16 vlan_tci)
+{
+ if (!hmap_is_empty(&ofproto->realdev_vid_map)) {
+ uint16_t realdev_ofp_port = odp_port_to_ofp_port(realdev_odp_port);
+ int vid = vlan_tci_to_vid(vlan_tci);
+ const struct vlan_splinter *vsp;
+
+ HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node,
+ hash_realdev_vid(realdev_ofp_port, vid),
+ &ofproto->realdev_vid_map) {
+ if (vsp->realdev_ofp_port == realdev_ofp_port
+ && vsp->vid == vid) {
+ return ofp_port_to_odp_port(vsp->vlandev_ofp_port);
+ }
+ }
+ }
+ return realdev_odp_port;
+}
+
+static struct vlan_splinter *
+vlandev_find(const struct ofproto_dpif *ofproto, uint16_t vlandev_ofp_port)
+{
+ struct vlan_splinter *vsp;
+
+ HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node, hash_int(vlandev_ofp_port, 0),
+ &ofproto->vlandev_map) {
+ if (vsp->vlandev_ofp_port == vlandev_ofp_port) {
+ return vsp;
+ }
+ }
+
+ return NULL;
+}
+
+static uint16_t
+vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto,
+ uint16_t vlandev_ofp_port, int *vid)
+{
+ if (!hmap_is_empty(&ofproto->vlandev_map)) {
+ const struct vlan_splinter *vsp;
+
+ vsp = vlandev_find(ofproto, vlandev_ofp_port);
+ if (vsp) {
+ if (vid) {
+ *vid = vsp->vid;
+ }
+ return vsp->realdev_ofp_port;
+ }
+ }
+ return 0;
+}
+
+static void
+vsp_remove(struct ofport_dpif *port)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ struct vlan_splinter *vsp;
+
+ vsp = vlandev_find(ofproto, port->up.ofp_port);
+ if (vsp) {
+ hmap_remove(&ofproto->vlandev_map, &vsp->vlandev_node);
+ hmap_remove(&ofproto->realdev_vid_map, &vsp->realdev_vid_node);
+ free(vsp);
+
+ port->realdev_ofp_port = 0;
+ } else {
+ VLOG_ERR("missing vlan device record");
+ }
+}
+
+static void
+vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+
+ if (!vsp_vlandev_to_realdev(ofproto, port->up.ofp_port, NULL)
+ && (vsp_realdev_to_vlandev(ofproto, realdev_ofp_port, htons(vid))
+ == realdev_ofp_port)) {
+ struct vlan_splinter *vsp;
+
+ vsp = xmalloc(sizeof *vsp);
+ hmap_insert(&ofproto->vlandev_map, &vsp->vlandev_node,
+ hash_int(port->up.ofp_port, 0));
+ hmap_insert(&ofproto->realdev_vid_map, &vsp->realdev_vid_node,
+ hash_realdev_vid(realdev_ofp_port, vid));
+ vsp->realdev_ofp_port = realdev_ofp_port;
+ vsp->vlandev_ofp_port = port->up.ofp_port;
+ vsp->vid = vid;
+
+ port->realdev_ofp_port = realdev_ofp_port;
+ } else {
+ VLOG_ERR("duplicate vlan device record");
+ }