+ 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 = REV_RECONFIGURE;
+
+ 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);
+}
+
+/* Returns the ODP port number of the Linux VLAN device that corresponds to
+ * 'vlan_tci' on the network device with port number 'realdev_odp_port' in
+ * 'ofproto'. For example, given 'realdev_odp_port' of eth0 and 'vlan_tci' 9,
+ * it would return the port number of eth0.9.
+ *
+ * Unless VLAN splinters are enabled for port 'realdev_odp_port', this
+ * function just returns its 'realdev_odp_port' argument. */
+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;
+}
+
+/* Returns the OpenFlow port number of the "real" device underlying the Linux
+ * VLAN device with OpenFlow port number 'vlandev_ofp_port' and stores the
+ * VLAN VID of the Linux VLAN device in '*vid'. For example, given
+ * 'vlandev_ofp_port' of eth0.9, it would return the OpenFlow port number of
+ * eth0 and store 9 in '*vid'.
+ *
+ * Returns 0 and does not modify '*vid' if 'vlandev_ofp_port' is not a Linux
+ * VLAN device. Unless VLAN splinters are enabled, this is what this function
+ * always does.*/
+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;
+}
+
+/* Given 'flow', a flow representing a packet received on 'ofproto', checks
+ * whether 'flow->in_port' represents a Linux VLAN device. If so, changes
+ * 'flow->in_port' to the "real" device backing the VLAN device, sets
+ * 'flow->vlan_tci' to the VLAN VID, and returns true. Otherwise (which is
+ * always the case unless VLAN splinters are enabled), returns false without
+ * making any changes. */
+static bool
+vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow)
+{
+ uint16_t realdev;
+ int vid;
+
+ realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
+ if (!realdev) {
+ return false;
+ }
+
+ /* Cause the flow to be processed as if it came in on the real device with
+ * the VLAN device's VLAN ID. */
+ flow->in_port = realdev;
+ flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+ return true;
+}
+
+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");
+ }