packets: Use RARPs for learning packets.
authorEthan Jackson <ethan@nicira.com>
Thu, 7 Jun 2012 22:27:22 +0000 (15:27 -0700)
committerEthan Jackson <ethan@nicira.com>
Fri, 8 Jun 2012 00:25:30 +0000 (17:25 -0700)
Traditionally Open vSwitch had used 802.2 SNAP packets to update
upstream switch learning tables when necessary.  This approach had
advantages in that debugging information could be embedded in the
packet helping hapless admins figure out what's going on.  However,
since both qemu and VMware use RARP for this purpose, it seems
appropriate to fall in line with the defacto standard.

Requested-by: Ben Basler <bbasler@nicira.com>
Signed-off-by: Ethan Jackson <ethan@nicira.com>
NEWS
lib/bond.c
lib/packets.c
lib/packets.h
ofproto/fail-open.c
vswitchd/INTERNALS

diff --git a/NEWS b/NEWS
index 66bf72795533edb82edec73a8169915d4f2208f7..8cd38dd760405331d3a67061af63355a7df88ba4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,9 @@ post-v1.7.0
     - Added support for arbitrary ethernet masks
     - Additional protocols are not mirrored and dropped when forward-bpdu is
       false.  For a full list, see the ovs-vswitchd.conf.db man page.
+    - Open vSwitch now sends RARP packets in situations where it previously
+      sent a custom protocol, making it consistent with behavior of QEMU and
+      VMware.
 
 
 v1.7.0 - xx xxx xxxx
index cffdae249f63b45f04f43e9ba0127cd8b2cf9506..8b32bdf98c0920fcf71645ed995401ba5e06f2c6 100644 (file)
@@ -531,8 +531,7 @@ bond_compose_learning_packet(struct bond *bond,
     slave = choose_output_slave(bond, &flow, vlan);
 
     packet = ofpbuf_new(0);
-    compose_benign_packet(packet, "Open vSwitch Bond Failover", 0xf177,
-                          eth_src);
+    compose_benign_packet(packet, eth_src);
     if (vlan) {
         eth_push_vlan(packet, htons(vlan));
     }
index a8208f5986f89504e5502cfbca113e89e552ae96..9b61d047721f7120eeee014300fdd679d59d1a8f 100644 (file)
@@ -120,27 +120,37 @@ eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
     }
 }
 
-/* Fills 'b' with an 802.2 SNAP packet with Ethernet source address 'eth_src',
- * the Nicira OUI as SNAP organization and 'snap_type' as SNAP type.  The text
- * string in 'tag' is enclosed as the packet payload.
- *
+/* Fills 'b' with a Reverse ARP packet with Ethernet source address 'eth_src'.
  * This function is used by Open vSwitch to compose packets in cases where
- * context is important but content doesn't (or shouldn't) matter.  For this
- * purpose, 'snap_type' should be a random number and 'tag' should be an
- * English phrase that explains the purpose of the packet.  (The English phrase
- * gives hapless admins running Wireshark the opportunity to figure out what's
- * going on.) */
+ * context is important but content doesn't (or shouldn't) matter.
+ *
+ * The returned packet has enough headroom to insert an 802.1Q VLAN header if
+ * desired. */
 void
-compose_benign_packet(struct ofpbuf *b, const char *tag, uint16_t snap_type,
-                      const uint8_t eth_src[ETH_ADDR_LEN])
+compose_benign_packet(struct ofpbuf *b, const uint8_t eth_src[ETH_ADDR_LEN])
 {
-    size_t tag_size = strlen(tag) + 1;
-    char *payload;
+    struct eth_header *eth;
+    struct rarp_header *rarp;
 
-    payload = snap_compose(b, eth_addr_broadcast, eth_src, 0x002320, snap_type,
-                           tag_size + ETH_ADDR_LEN);
-    memcpy(payload, tag, tag_size);
-    memcpy(payload + tag_size, eth_src, ETH_ADDR_LEN);
+    ofpbuf_clear(b);
+    ofpbuf_prealloc_tailroom(b, ETH_HEADER_LEN + VLAN_HEADER_LEN
+                             + RARP_HEADER_LEN);
+    ofpbuf_reserve(b, VLAN_HEADER_LEN);
+    eth = ofpbuf_put_uninit(b, sizeof *eth);
+    memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
+    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+    eth->eth_type = htons(ETH_TYPE_RARP);
+
+    rarp = ofpbuf_put_uninit(b, sizeof *rarp);
+    rarp->hw_addr_space = htons(ARP_HTYPE_ETH);
+    rarp->proto_addr_space = htons(ETH_TYPE_IP);
+    rarp->hw_addr_length = ETH_ADDR_LEN;
+    rarp->proto_addr_length = sizeof rarp->src_proto_addr;
+    rarp->opcode = htons(RARP_REQUEST_REVERSE);
+    memcpy(rarp->src_hw_addr, eth_src, ETH_ADDR_LEN);
+    rarp->src_proto_addr = htonl(0);
+    memcpy(rarp->target_hw_addr, eth_src, ETH_ADDR_LEN);
+    rarp->target_proto_addr = htonl(0);
 }
 
 /* Insert VLAN header according to given TCI. Packet passed must be Ethernet
@@ -424,49 +434,6 @@ eth_compose(struct ofpbuf *b, const uint8_t eth_dst[ETH_ADDR_LEN],
     return data;
 }
 
-/* Populates 'b' with an Ethernet LLC+SNAP packet headed with the given
- * 'eth_dst', 'eth_src', 'snap_org', and 'snap_type'.  A payload of 'size'
- * bytes is allocated in 'b' and returned.  This payload may be populated with
- * appropriate information by the caller.
- *
- * The returned packet has enough headroom to insert an 802.1Q VLAN header if
- * desired. */
-void *
-snap_compose(struct ofpbuf *b, const uint8_t eth_dst[ETH_ADDR_LEN],
-             const uint8_t eth_src[ETH_ADDR_LEN],
-             unsigned int oui, uint16_t snap_type, size_t size)
-{
-    struct eth_header *eth;
-    struct llc_snap_header *llc_snap;
-    void *payload;
-
-    /* Compose basic packet structure.  (We need the payload size to stick into
-     * the 802.2 header.) */
-    ofpbuf_clear(b);
-    ofpbuf_prealloc_tailroom(b, ETH_HEADER_LEN + VLAN_HEADER_LEN
-                             + LLC_SNAP_HEADER_LEN + size);
-    ofpbuf_reserve(b, VLAN_HEADER_LEN);
-    eth = ofpbuf_put_zeros(b, ETH_HEADER_LEN);
-    llc_snap = ofpbuf_put_zeros(b, LLC_SNAP_HEADER_LEN);
-    payload = ofpbuf_put_uninit(b, size);
-
-    /* Compose 802.2 header. */
-    memcpy(eth->eth_dst, eth_dst, ETH_ADDR_LEN);
-    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
-    eth->eth_type = htons(b->size - ETH_HEADER_LEN);
-
-    /* Compose LLC, SNAP headers. */
-    llc_snap->llc.llc_dsap = LLC_DSAP_SNAP;
-    llc_snap->llc.llc_ssap = LLC_SSAP_SNAP;
-    llc_snap->llc.llc_cntl = LLC_CNTL_SNAP;
-    llc_snap->snap.snap_org[0] = oui >> 16;
-    llc_snap->snap.snap_org[1] = oui >> 8;
-    llc_snap->snap.snap_org[2] = oui;
-    llc_snap->snap.snap_type = htons(snap_type);
-
-    return payload;
-}
-
 static void
 packet_set_ipv4_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr)
 {
index 4a0fcae14ce2026689e35113bae9b197aa02f312..c1d404d45b538dabbc4173e02561bc0670e5e0d3 100644 (file)
@@ -135,8 +135,7 @@ static inline void eth_addr_nicira_random(uint8_t ea[ETH_ADDR_LEN])
 bool eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN]);
 bool eth_addr_from_string(const char *, uint8_t ea[ETH_ADDR_LEN]);
 
-void compose_benign_packet(struct ofpbuf *, const char *tag,
-                           uint16_t snap_type,
+void compose_benign_packet(struct ofpbuf *,
                            const uint8_t eth_src[ETH_ADDR_LEN]);
 
 void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
@@ -182,6 +181,7 @@ void eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN],
 #define ETH_TYPE_VLAN          0x8100
 #define ETH_TYPE_IPV6          0x86dd
 #define ETH_TYPE_LACP          0x8809
+#define ETH_TYPE_RARP          0x8035
 
 /* Minimum value for an Ethernet type.  Values below this are IEEE 802.2 frame
  * lengths. */
@@ -228,6 +228,25 @@ struct llc_snap_header {
 } __attribute__((packed));
 BUILD_ASSERT_DECL(LLC_SNAP_HEADER_LEN == sizeof(struct llc_snap_header));
 
+#define ARP_HTYPE_ETH 0x0001
+#define RARP_REQUEST_REVERSE 0x0003
+
+#define RARP_HEADER_LEN 28
+/* RARP header only for Ethernet-IP. */
+struct rarp_header {
+    ovs_be16 hw_addr_space;        /* ARP_HTYPE_ETH. */
+    ovs_be16 proto_addr_space;     /* ETH_TYPE_IP. */
+    uint8_t hw_addr_length;        /* ETH_ADDR_LEN. */
+    uint8_t proto_addr_length;     /* IPV4_ADDR_LEN. */
+    ovs_be16 opcode;               /* RARP_REQUEST_REVERSE. */
+    uint8_t src_hw_addr[ETH_ADDR_LEN];
+    ovs_be32 src_proto_addr;
+    uint8_t target_hw_addr[ETH_ADDR_LEN];
+    ovs_be32 target_proto_addr;
+} __attribute__((packed));
+BUILD_ASSERT_DECL(RARP_HEADER_LEN == sizeof(struct rarp_header));
+
+
 #define VLAN_VID_MASK 0x0fff
 #define VLAN_VID_SHIFT 0
 
index 99bbccd3178f88a1b66fb5bf1043b4bd9bb9b190..f3a99c131f6328ecfa92b46e3e55f8d81948e247 100644 (file)
@@ -121,7 +121,7 @@ send_bogus_packet_ins(struct fail_open *fo)
 
     ofpbuf_init(&b, 128);
     eth_addr_nicira_random(mac);
-    compose_benign_packet(&b, "Open vSwitch Controller Probe", 0xa033, mac);
+    compose_benign_packet(&b, mac);
 
     memset(&pin, 0, sizeof pin);
     pin.packet = b.data;
index 645752d0ce66a84d156eac5cb2b91819b3964c0b..7aa3c29677a1fb3a52304385f901b4f7904cf315 100644 (file)
@@ -57,14 +57,15 @@ enabled all output packets are blackholed anyway.
 
 When a slave becomes disabled, the vswitch immediately chooses a new
 output port for traffic that was destined for that slave (see
-bond_enable_slave()).  It also sends a "gratuitous learning packet" on
-the bond port (on the newly chosen slave) for each MAC address that
-the vswitch has learned on a port other than the bond (see
-bond_send_learning_packets()), to teach the physical switch that the
-new slave should be used in place of the one that is now disabled.
-(This behavior probably makes sense only for a vswitch that has only
-one port (the bond) connected to a physical switch; vswitchd should
-probably provide a way to disable or configure it in other scenarios.)
+bond_enable_slave()).  It also sends a "gratuitous learning packet",
+specifically a RARP, on the bond port (on the newly chosen slave) for
+each MAC address that the vswitch has learned on a port other than the
+bond (see bond_send_learning_packets()), to teach the physical switch
+that the new slave should be used in place of the one that is now
+disabled.  (This behavior probably makes sense only for a vswitch that
+has only one port (the bond) connected to a physical switch; vswitchd
+should probably provide a way to disable or configure it in other
+scenarios.)
 
 Bond Packet Input
 -----------------