diff --git a/ChangeLog b/ChangeLog index 024a876dd..2239f72d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -168,6 +168,25 @@ 2015-01-19 Kris Moore * grub-core/disk/geli.c: Support GELI v6 and v7. +======= +2015-01-23 Paulo Flabiano Smorigo + + Add Virtual LAN support. + + This patch adds support for virtual LAN (VLAN) tagging. VLAN tagging + allows multiple VLANs in a bridged network to share the same physical + network link but maintain isolation: + + http://en.wikipedia.org/wiki/IEEE_802.1Q + + * grub-core/net/ethernet.c: Add check, get, and set vlan tag id. + * grub-core/net/drivers/ieee1275/ofnet.c: Get vlan tag id from + bootargs. + * grub-core/net/arp.c: Add check. + * grub-core/net/ip.c: Likewise. + * include/grub/net/arp.h: Add vlantag attribute. + * include/grub/net/ip.h: Likewise. +>>>>>>> Add Virtual LAN support 2014-12-09 Andrei Borzenkov diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index 8cc390b0e..00d37b6c3 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -122,8 +122,8 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, } grub_err_t -grub_net_arp_receive (struct grub_net_buff *nb, - struct grub_net_card *card) +grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, + grub_uint16_t vlantag_vid) { struct arphdr *arp_header = (struct arphdr *) nb->data; grub_uint8_t *sender_hardware_address; @@ -158,6 +158,12 @@ grub_net_arp_receive (struct grub_net_buff *nb, FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { + /* Check vlantag id */ + if (inf->card == card && + ((inf->vlantag.set && vlantag_vid != inf->vlantag.vid) || + (!inf->vlantag.set && vlantag_vid != 0))) + continue; + /* Am I the protocol address target? */ if (grub_net_addr_cmp (&inf->address, &target_addr) == 0 && grub_be_to_cpu16 (arp_header->op) == ARP_REQUEST) diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index eea8e71d3..7eefb9aed 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -147,11 +147,11 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, char *comma_char = 0; char *equal_char = 0; grub_size_t field_counter = 0; - grub_net_network_level_address_t client_addr, gateway_addr, subnet_mask; grub_net_link_level_address_t hw_addr; grub_net_interface_flags_t flags = 0; struct grub_net_network_level_interface *inter; + grub_uint32_t vlantag = 0; hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; @@ -169,6 +169,18 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, *equal_char = 0; grub_env_set_net_property ((*card)->name, args, equal_char + 1, grub_strlen(equal_char + 1)); + + if ((grub_strcmp (args, "vtag") == 0) && + (grub_strlen (equal_char + 1) > 4)) + { + vlantag = grub_strtoul (equal_char + 1, 0, 16) & 0xffff; + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + vlantag = 0; + grub_errno = GRUB_ERR_NONE; + } + } + *equal_char = '='; } else @@ -207,6 +219,12 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, hw_addr.mac, sizeof(hw_addr.mac), 0); inter = grub_net_add_addr ((*card)->name, *card, &client_addr, &hw_addr, flags); + if (vlantag > 0) + { + inter->vlantag.set = 1; + inter->vlantag.vid = vlantag & 0xfff; + } + grub_net_add_ipv4_local (inter, __builtin_ctz (~grub_le_to_cpu32 (subnet_mask.ipv4))); } diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index c397b1b34..3a526b3bf 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -56,10 +57,16 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, { struct etherhdr *eth; grub_err_t err; + grub_uint8_t etherhdr_size; - COMPILE_TIME_ASSERT (sizeof (*eth) < GRUB_NET_MAX_LINK_HEADER_SIZE); + etherhdr_size = sizeof (*eth); + COMPILE_TIME_ASSERT (sizeof (*eth) + 4 < GRUB_NET_MAX_LINK_HEADER_SIZE); - err = grub_netbuff_push (nb, sizeof (*eth)); + /* Increase ethernet header in case of vlantag */ + if (inf->vlantag.set) + etherhdr_size += 4; + + err = grub_netbuff_push (nb, etherhdr_size); if (err) return err; eth = (struct etherhdr *) nb->data; @@ -76,6 +83,21 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, return err; inf->card->opened = 1; } + + /* Check and add a vlan-tag if needed. */ + if (inf->vlantag.set) + { + grub_uint32_t vlantag; + vlantag = grub_cpu_to_be32 ((VLANTAG_IDENTIFIER << 16) | inf->vlantag.vid); + + /* Move eth type to the right */ + grub_memcpy ((char *) nb->data + etherhdr_size - 2, + (char *) nb->data + etherhdr_size - 6, 2); + + /* Add the tag in the middle */ + grub_memcpy ((char *) nb->data + etherhdr_size - 6, &vlantag, 4); + } + return inf->card->driver->send (inf->card, nb); } @@ -90,10 +112,26 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb, grub_net_link_level_address_t hwaddress; grub_net_link_level_address_t src_hwaddress; grub_err_t err; + grub_uint8_t etherhdr_size = sizeof (*eth); + grub_uint16_t vlantag_vid = 0; + + /* Check if a vlan-tag is present. If so, the ethernet header is 4 bytes */ + /* longer than the original one. The vlantag id is extracted and the header */ + /* is reseted to the original size. */ + if (grub_get_unaligned16 (nb->data + etherhdr_size - 2) == + grub_cpu_to_be16_compile_time (VLANTAG_IDENTIFIER)) + { + vlantag_vid = grub_be_to_cpu16 (grub_get_unaligned16 (nb->data + + etherhdr_size)) & 0x1fff; + etherhdr_size += 4; + /* Move eth type to the original position */ + grub_memcpy((char *) nb->data + etherhdr_size - 6, + (char *) nb->data + etherhdr_size - 2, 2); + } eth = (struct etherhdr *) nb->data; type = grub_be_to_cpu16 (eth->type); - err = grub_netbuff_pull (nb, sizeof (*eth)); + err = grub_netbuff_pull (nb, etherhdr_size); if (err) return err; @@ -121,13 +159,14 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb, { /* ARP packet. */ case GRUB_NET_ETHERTYPE_ARP: - grub_net_arp_receive (nb, card); + grub_net_arp_receive (nb, card, vlantag_vid); grub_netbuff_free (nb); return GRUB_ERR_NONE; /* IP packet. */ case GRUB_NET_ETHERTYPE_IP: case GRUB_NET_ETHERTYPE_IP6: - return grub_net_recv_ip_packets (nb, card, &hwaddress, &src_hwaddress); + return grub_net_recv_ip_packets (nb, card, &hwaddress, &src_hwaddress, + vlantag_vid); } grub_netbuff_free (nb); return GRUB_ERR_NONE; diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 5a6095444..a2addb3f6 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -225,12 +225,13 @@ handle_dgram (struct grub_net_buff *nb, grub_net_ip_protocol_t proto, const grub_net_network_level_address_t *source, const grub_net_network_level_address_t *dest, + grub_uint16_t vlantag_vid, grub_uint8_t ttl) { struct grub_net_network_level_interface *inf = NULL; grub_err_t err; int multicast = 0; - + /* DHCP needs special treatment since we don't know IP yet. */ { struct udphdr *udph; @@ -290,6 +291,13 @@ handle_dgram (struct grub_net_buff *nb, && grub_net_addr_cmp (&inf->address, dest) == 0 && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) break; + + /* Check vlantag id */ + if (inf->card == card && + ((inf->vlantag.set && vlantag_vid != inf->vlantag.vid) || + (!inf->vlantag.set && vlantag_vid != 0))) + continue; + /* Solicited node multicast. */ if (inf->card == card && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 @@ -378,7 +386,8 @@ static grub_err_t grub_net_recv_ip4_packets (struct grub_net_buff *nb, struct grub_net_card *card, const grub_net_link_level_address_t *hwaddress, - const grub_net_link_level_address_t *src_hwaddress) + const grub_net_link_level_address_t *src_hwaddress, + grub_uint16_t vlantag_vid) { struct iphdr *iph = (struct iphdr *) nb->data; grub_err_t err; @@ -453,7 +462,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff *nb, dest.ipv4 = iph->dest; return handle_dgram (nb, card, src_hwaddress, hwaddress, iph->protocol, - &source, &dest, iph->ttl); + &source, &dest, vlantag_vid, iph->ttl); } for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev) @@ -589,7 +598,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff *nb, dest.ipv4 = dst; return handle_dgram (ret, card, src_hwaddress, - hwaddress, proto, &source, &dest, + hwaddress, proto, &source, &dest, vlantag_vid, ttl); } } @@ -644,7 +653,8 @@ static grub_err_t grub_net_recv_ip6_packets (struct grub_net_buff *nb, struct grub_net_card *card, const grub_net_link_level_address_t *hwaddress, - const grub_net_link_level_address_t *src_hwaddress) + const grub_net_link_level_address_t *src_hwaddress, + grub_uint16_t vlantag_vid) { struct ip6hdr *iph = (struct ip6hdr *) nb->data; grub_err_t err; @@ -695,21 +705,24 @@ grub_net_recv_ip6_packets (struct grub_net_buff *nb, grub_memcpy (dest.ipv6, &iph->dest, sizeof (dest.ipv6)); return handle_dgram (nb, card, src_hwaddress, hwaddress, iph->protocol, - &source, &dest, iph->ttl); + &source, &dest, vlantag_vid, iph->ttl); } grub_err_t grub_net_recv_ip_packets (struct grub_net_buff *nb, struct grub_net_card *card, const grub_net_link_level_address_t *hwaddress, - const grub_net_link_level_address_t *src_hwaddress) + const grub_net_link_level_address_t *src_hwaddress, + grub_uint16_t vlantag_vid) { struct iphdr *iph = (struct iphdr *) nb->data; if ((iph->verhdrlen >> 4) == 4) - return grub_net_recv_ip4_packets (nb, card, hwaddress, src_hwaddress); + return grub_net_recv_ip4_packets (nb, card, hwaddress, src_hwaddress, + vlantag_vid); if ((iph->verhdrlen >> 4) == 6) - return grub_net_recv_ip6_packets (nb, card, hwaddress, src_hwaddress); + return grub_net_recv_ip6_packets (nb, card, hwaddress, src_hwaddress, + vlantag_vid); grub_dprintf ("net", "Bad IP version: %d\n", (iph->verhdrlen >> 4)); grub_netbuff_free (nb); return GRUB_ERR_NONE; diff --git a/include/grub/net.h b/include/grub/net.h index 538baa33e..66923e68c 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -268,6 +268,12 @@ typedef struct grub_net extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name); +struct grub_net_vlantag +{ + grub_uint8_t set; + grub_uint16_t vid; +}; + struct grub_net_network_level_interface { struct grub_net_network_level_interface *next; @@ -279,6 +285,7 @@ struct grub_net_network_level_interface grub_net_interface_flags_t flags; struct grub_net_bootp_packet *dhcp_ack; grub_size_t dhcp_acklen; + struct grub_net_vlantag vlantag; void *data; }; @@ -538,4 +545,6 @@ extern char *grub_net_default_server; #define GRUB_NET_INTERVAL 400 #define GRUB_NET_INTERVAL_ADDITION 20 +#define VLANTAG_IDENTIFIER 0x8100 + #endif /* ! GRUB_NET_HEADER */ diff --git a/include/grub/net/arp.h b/include/grub/net/arp.h index bb1703622..56336b356 100644 --- a/include/grub/net/arp.h +++ b/include/grub/net/arp.h @@ -22,10 +22,11 @@ #include extern grub_err_t grub_net_arp_receive (struct grub_net_buff *nb, - struct grub_net_card *card); + struct grub_net_card *card, + grub_uint16_t vlantag_vid); grub_err_t grub_net_arp_send_request (struct grub_net_network_level_interface *inf, - const grub_net_network_level_address_t *proto_addr); + const grub_net_network_level_address_t *proto_addr); #endif diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index dcceaa568..8cd0bfdc0 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -48,7 +48,8 @@ grub_err_t grub_net_recv_ip_packets (struct grub_net_buff *nb, struct grub_net_card *card, const grub_net_link_level_address_t *hwaddress, - const grub_net_link_level_address_t *src_hwaddress); + const grub_net_link_level_address_t *src_hwaddress, + grub_uint16_t vlantag_vid); grub_err_t grub_net_send_ip_packet (struct grub_net_network_level_interface *inf,