+
+#define ARP_PACKET_SIZE (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \
+ ARP_ETH_HEADER_LEN)
+
+/* Clears 'b' and replaces its contents by an ARP frame with the specified
+ * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'. The outer
+ * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination
+ * 'arp_tha', except that destination ff:ff:ff:ff:ff:ff is used instead if
+ * 'broadcast' is true. */
+void
+compose_arp(struct dp_packet *b, uint16_t arp_op,
+ const uint8_t arp_sha[ETH_ADDR_LEN],
+ const uint8_t arp_tha[ETH_ADDR_LEN], bool broadcast,
+ ovs_be32 arp_spa, ovs_be32 arp_tpa)
+{
+ struct eth_header *eth;
+ struct arp_eth_header *arp;
+
+ dp_packet_clear(b);
+ dp_packet_prealloc_tailroom(b, ARP_PACKET_SIZE);
+ dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
+
+ eth = dp_packet_put_uninit(b, sizeof *eth);
+ memcpy(eth->eth_dst, broadcast ? eth_addr_broadcast : arp_tha,
+ ETH_ADDR_LEN);
+ memcpy(eth->eth_src, arp_sha, ETH_ADDR_LEN);
+ eth->eth_type = htons(ETH_TYPE_ARP);
+
+ arp = dp_packet_put_uninit(b, sizeof *arp);
+ arp->ar_hrd = htons(ARP_HRD_ETHERNET);
+ arp->ar_pro = htons(ARP_PRO_IP);
+ arp->ar_hln = sizeof arp->ar_sha;
+ arp->ar_pln = sizeof arp->ar_spa;
+ arp->ar_op = htons(arp_op);
+ memcpy(arp->ar_sha, arp_sha, ETH_ADDR_LEN);
+ memcpy(arp->ar_tha, arp_tha, ETH_ADDR_LEN);
+
+ put_16aligned_be32(&arp->ar_spa, arp_spa);
+ put_16aligned_be32(&arp->ar_tpa, arp_tpa);
+
+ dp_packet_reset_offsets(b);
+ dp_packet_set_l3(b, arp);
+}
+
+uint32_t
+packet_csum_pseudoheader(const struct ip_header *ip)
+{
+ uint32_t partial = 0;
+
+ partial = csum_add32(partial, get_16aligned_be32(&ip->ip_src));
+ partial = csum_add32(partial, get_16aligned_be32(&ip->ip_dst));
+ partial = csum_add16(partial, htons(ip->ip_proto));
+ partial = csum_add16(partial, htons(ntohs(ip->ip_tot_len) -
+ IP_IHL(ip->ip_ihl_ver) * 4));
+
+ return partial;
+}