"Fossies" - the Fresh Open Source Software Archive

Member "open-fcoe-3.19/fcoe-utils/lib/fip.c" (15 Apr 2015, 11022 Bytes) of package /linux/misc/open-fcoe-3.19.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fip.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.11_vs_3.19.

    1 /*
    2  * Copyright(c) 2010 Intel Corporation. All rights reserved.
    3  *
    4  * This program is free software; you can redistribute it and/or modify it
    5  * under the terms and conditions of the GNU General Public License,
    6  * version 2, as published by the Free Software Foundation.
    7  *
    8  * This program is distributed in the hope it will be useful, but WITHOUT
    9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   11  * more details.
   12  *
   13  * You should have received a copy of the GNU General Public License along with
   14  * this program; if not, write to the Free Software Foundation, Inc.,
   15  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
   16  *
   17  * Maintained at www.Open-FCoE.org
   18  */
   19 
   20 /* Routines for automatic FIP VLAN discovery and creation */
   21 /* Shared by fcoemon and fipvlan */
   22 
   23 #include <string.h>
   24 #include <stdlib.h>
   25 #include <stdio.h>
   26 #include <stdarg.h>
   27 #include <stdint.h>
   28 #include <stdbool.h>
   29 #include <unistd.h>
   30 #include <errno.h>
   31 #include <getopt.h>
   32 #include <poll.h>
   33 #include <sys/socket.h>
   34 #include <sys/ioctl.h>
   35 #include <net/if.h>
   36 #include <net/if_arp.h>
   37 #include <net/ethernet.h>
   38 #include <arpa/inet.h>
   39 #include <linux/netlink.h>
   40 #include <linux/rtnetlink.h>
   41 #include <linux/if_packet.h>
   42 #include "fip.h"
   43 #include "fcoemon_utils.h"
   44 #include "rtnetlink.h"
   45 
   46 #define FIP_LOG(...)            sa_log(__VA_ARGS__)
   47 #define FIP_LOG_ERR(error, ...)     sa_log_err(error, __func__, __VA_ARGS__)
   48 #define FIP_LOG_ERRNO(...)      sa_log_err(errno, __func__, __VA_ARGS__)
   49 #define FIP_LOG_DBG(...)        sa_log_debug(__VA_ARGS__)
   50 
   51 static int fip_mac_is_valid(unsigned char *mac)
   52 {
   53     if (0x01 & mac[0])
   54         return 0;
   55     return !!(mac[0] | mac[1] | mac[2] | mac[3] | mac[4] | mac[5]);
   56 }
   57 
   58 /**
   59  * fip_get_sanmac - get SAN MAC through dcbnl interface
   60  * @ifindex: network interface index to send on
   61  * @addr: output buffer to the SAN MAC address
   62  *
   63  * Returns 0 for success, none 0 for failure
   64  */
   65 static int fip_get_sanmac(int ifindex, unsigned char *addr)
   66 {
   67     int s;
   68     int rc = -EIO;
   69     struct ifreq ifr;
   70 
   71     memset(addr, 0, ETHER_ADDR_LEN);
   72     s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
   73     if (s < 0)
   74         return s;
   75 
   76     memset(&ifr, 0, sizeof(ifr));
   77     ifr.ifr_ifindex = ifindex;
   78     rc = ioctl(s, SIOCGIFNAME, &ifr);
   79     close(s);
   80     if (rc)
   81         return rc;
   82 
   83     rc = rtnl_get_sanmac(ifr.ifr_name, addr);
   84     if (rc)
   85         return rc;
   86 
   87     return !fip_mac_is_valid(addr);
   88 }
   89 
   90 /**
   91  * fip_socket_add_addr - add a MAC address to the input socket
   92  * @s: ETH_P_FIP packet socket to setsockopt on
   93  * @ifindex: network interface index to send on
   94  * @add: true to add false to del
   95  * @mac: MAC address to add or delete
   96  * @multi: false if unicast, true if multicast address
   97  */
   98 static int
   99 fip_socket_add_addr(int s, int ifindex, bool add, const __u8 *mac, bool multi)
  100 {
  101     struct packet_mreq mr;
  102     int rc = 0;
  103 
  104     memset(&mr, 0, sizeof(mr));
  105     mr.mr_ifindex = ifindex;
  106     mr.mr_type = multi ? PACKET_MR_MULTICAST : PACKET_MR_UNICAST;
  107     mr.mr_alen = ETHER_ADDR_LEN;
  108     memcpy(mr.mr_address, mac, ETHER_ADDR_LEN);
  109     if (setsockopt(s, SOL_PACKET,
  110                add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
  111                &mr, sizeof(mr)) < 0) {
  112         FIP_LOG_DBG("PACKET_%s_MEMBERSHIP:failed\n",
  113                 add ? "ADD" : "DROP");
  114         rc = -errno;
  115     }
  116     return rc;
  117 }
  118 
  119 /**
  120  * fip_socket_sanmac - add SAN MAC to the unicast list for input socket
  121  * @s: ETH_P_FIP packet socket to setsockopt on
  122  * @ifindex: network interface index to send on
  123  * @add: 1 to add 0 to del
  124  */
  125 static int fip_socket_sanmac(int s, int ifindex, int add)
  126 {
  127     unsigned char smac[ETHER_ADDR_LEN];
  128 
  129     if (fip_get_sanmac(ifindex, smac)) {
  130         FIP_LOG_DBG("%s: no sanmac, ifindex %d\n", __func__, ifindex);
  131         return -ENXIO;
  132     }
  133 
  134     return fip_socket_add_addr(s, ifindex, add, smac, false);
  135 }
  136 
  137 /**
  138  * fip_socket_multi - add multicast MAC address to the input socket
  139  * @s: ETH_P_FIP packet socket to setsockopt on
  140  * @ifindex: network interface index to send on
  141  * @add: true to add, false to del
  142  * @multi: Multicast destination
  143  */
  144 static void
  145 fip_socket_multi(int s, int ifindex, bool add, enum fip_multi multi)
  146 {
  147     __u8 smac[ETHER_ADDR_LEN] = FIP_ALL_FCOE_MACS;
  148 
  149     smac[ETHER_ADDR_LEN - 1] = multi;
  150     fip_socket_add_addr(s, ifindex, add, smac, true);
  151 }
  152 
  153 /**
  154  * fip_ethhdr - fills up the ethhdr for FIP
  155  * @ifindex: network interface index to send on
  156  * @mac: mac address of the sending network interface
  157  * @eh: buffer for ether header
  158  * @dest: destination selector
  159  *
  160  * Note: assuming no VLAN
  161  */
  162 static void fip_ethhdr(int ifindex, const unsigned char *mac, struct ethhdr *eh,
  163                enum fip_multi multi)
  164 {
  165     unsigned char smac[ETHER_ADDR_LEN];
  166     unsigned char dmac[ETHER_ADDR_LEN] = FIP_ALL_FCF_MACS;
  167     if (fip_get_sanmac(ifindex, smac))
  168         memcpy(smac, mac, ETHER_ADDR_LEN);
  169 
  170     dmac[ETHER_ADDR_LEN - 1] = multi;
  171     eh->h_proto = htons(ETH_P_FIP);
  172     memcpy(eh->h_source, smac, ETHER_ADDR_LEN);
  173     memcpy(eh->h_dest, dmac, ETHER_ADDR_LEN);
  174 }
  175 
  176 /**
  177  * drain_socket - Discard receive packets on a socket
  178  */
  179 static void drain_socket(int s)
  180 {
  181     char buf[4096];
  182     struct sockaddr_ll sa;
  183     struct iovec iov[] = {
  184         { .iov_base = buf, .iov_len = sizeof(buf), },
  185     };
  186     struct msghdr msg = {
  187         .msg_name = &sa,
  188         .msg_namelen = sizeof(sa),
  189         .msg_iov = iov,
  190         .msg_iovlen = ARRAY_SIZE(iov),
  191     };
  192 
  193     while (recvmsg(s, &msg, MSG_DONTWAIT) > 0) {
  194         /* Drop the packet */
  195     }
  196 }
  197 
  198 /**
  199  * fip_socket - create and bind a packet socket for FIP
  200  * @ifindex: ifindex of netdevice to bind to
  201  * @multi: Indication of any multicast address to bind to
  202  */
  203 int fip_socket(int ifindex, enum fip_multi multi)
  204 {
  205     struct sockaddr_ll sa = {
  206         .sll_family = AF_PACKET,
  207         .sll_protocol = htons(ETH_P_FIP),
  208         .sll_ifindex = ifindex,
  209     };
  210     int s;
  211     int rc;
  212 
  213     s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_FIP));
  214     if (s < 0)
  215         return s;
  216 
  217     rc = fip_socket_sanmac(s, ifindex, 1);
  218     if (rc < 0) {
  219         close(s);
  220         return rc;
  221     }
  222     if (multi != FIP_NONE)
  223         fip_socket_multi(s, ifindex, true, multi);
  224 
  225     rc = bind(s, (struct sockaddr *) &sa, sizeof(sa));
  226     if (rc < 0) {
  227         close(s);
  228         return rc;
  229     }
  230 
  231     /*
  232      * Drain the packets that were received between socket and bind. We
  233      * could've received packets not meant for our interface. This can
  234      * interfere with vlan discovery
  235      */
  236     drain_socket(s);
  237 
  238     return s;
  239 }
  240 
  241 
  242 /**
  243  * fip_send_vlan_request - send a FIP VLAN request
  244  * @s: ETH_P_FIP packet socket to send on
  245  * @ifindex: network interface index to send on
  246  * @mac: mac address of the sending network interface
  247  * @dest: destination selector
  248  *
  249  * Note: sends to address selected by @dest
  250  */
  251 ssize_t fip_send_vlan_request(int s, int ifindex, const unsigned char *mac,
  252                   enum fip_multi dest)
  253 {
  254     struct sockaddr_ll sa = {
  255         .sll_family = AF_PACKET,
  256         .sll_protocol = htons(ETH_P_FIP),
  257         .sll_ifindex = ifindex,
  258         .sll_hatype = ARPHRD_ETHER,
  259         .sll_pkttype = PACKET_MULTICAST,
  260         .sll_halen = ETHER_ADDR_LEN,
  261         .sll_addr = FIP_ALL_FCF_MACS,
  262     };
  263     struct fiphdr fh = {
  264         .fip_version = FIP_VERSION(1),
  265         .fip_proto = htons(FIP_PROTO_VLAN),
  266         .fip_subcode = FIP_VLAN_REQ,
  267         .fip_desc_len = htons(2),
  268         .fip_flags = 0,
  269     };
  270     struct {
  271         struct fip_tlv_mac_addr mac;
  272     } tlvs = {
  273         .mac = {
  274             .hdr = {
  275                 .tlv_type = FIP_TLV_MAC_ADDR,
  276                 .tlv_len = 2,
  277             },
  278         },
  279     };
  280     struct ethhdr eh;
  281     struct iovec iov[] = {
  282         { .iov_base = &eh, .iov_len = sizeof(eh), },
  283         { .iov_base = &fh, .iov_len = sizeof(fh), },
  284         { .iov_base = &tlvs, .iov_len = sizeof(tlvs), },
  285     };
  286     struct msghdr msg = {
  287         .msg_name = &sa,
  288         .msg_namelen = sizeof(sa),
  289         .msg_iov = iov,
  290         .msg_iovlen = ARRAY_SIZE(iov),
  291     };
  292     int rc;
  293 
  294     fip_ethhdr(ifindex, mac, &eh, dest);
  295     sa.sll_addr[ETHER_ADDR_LEN - 1] = dest;
  296     memcpy(tlvs.mac.mac_addr, eh.h_source, ETHER_ADDR_LEN);
  297     FIP_LOG_DBG("sending FIP VLAN request");
  298     rc = sendmsg(s, &msg, 0);
  299     if (rc < 0) {
  300         rc = -errno;
  301         FIP_LOG_ERRNO("sendmsg error");
  302     }
  303     return rc;
  304 }
  305 
  306 /**
  307  * fip_send_vlan_notification - send a FIP VLAN notification
  308  * @s: ETH_P_FIP packet socket to send on
  309  * @ifindex: network interface index to send on
  310  * @mac: mac address of the sending network interface
  311  * @dest: destination mac address
  312  * @tlvs: pointer to vlan tlvs to send
  313  * @tlv_count: number of vlan tlvs to send
  314  */
  315 int
  316 fip_send_vlan_notification(int s, int ifindex, const __u8 *mac,
  317                const __u8 *dest, struct fip_tlv_vlan *vtlvs,
  318                int vlan_count)
  319 {
  320     struct sockaddr_ll dsa = {
  321         .sll_family = AF_PACKET,
  322         .sll_protocol = htons(ETH_P_FIP),
  323         .sll_ifindex = ifindex,
  324         .sll_hatype = ARPHRD_ETHER,
  325         .sll_pkttype = PACKET_OTHERHOST,
  326         .sll_halen = ETHER_ADDR_LEN,
  327     };
  328     struct fiphdr sfh = {
  329         .fip_version = FIP_VERSION(1),
  330         .fip_proto = htons(FIP_PROTO_VLAN),
  331         .fip_subcode = FIP_VLAN_NOTE_VN2VN,
  332         .fip_desc_len = htons(2),
  333         .fip_flags = 0,
  334     };
  335     struct {
  336         struct fip_tlv_mac_addr mac;
  337     } tlvs = {
  338         .mac = {
  339             .hdr = {
  340                 .tlv_type = FIP_TLV_MAC_ADDR,
  341                 .tlv_len = 2,
  342             },
  343         },
  344     };
  345     struct ethhdr eh;
  346     struct iovec iov[] = {
  347         { .iov_base = &eh, .iov_len = sizeof(eh), },
  348         { .iov_base = &sfh, .iov_len = sizeof(sfh), },
  349         { .iov_base = &tlvs, .iov_len = sizeof(tlvs), },
  350         { .iov_base = NULL, .iov_len = 0, },
  351     };
  352     struct msghdr msg = {
  353         .msg_name = &dsa,
  354         .msg_namelen = sizeof(dsa),
  355         .msg_iov = iov,
  356         .msg_iovlen = ARRAY_SIZE(iov) - 1,
  357     };
  358     int rc;
  359 
  360     if (vlan_count) {
  361         sfh.fip_desc_len = htons(3);
  362         iov[3].iov_base = vtlvs;
  363         iov[3].iov_len = vlan_count * sizeof(*vtlvs);
  364         msg.msg_iovlen = ARRAY_SIZE(iov);
  365     }
  366 
  367     if (fip_get_sanmac(ifindex, eh.h_source))
  368         memcpy(eh.h_source, mac, ETHER_ADDR_LEN);
  369     eh.h_proto = htons(ETH_P_FIP);
  370     memcpy(dsa.sll_addr, dest, ETHER_ADDR_LEN);
  371     memcpy(eh.h_dest, dest, ETHER_ADDR_LEN);
  372     memcpy(tlvs.mac.mac_addr, eh.h_source, ETHER_ADDR_LEN);
  373     rc = sendmsg(s, &msg, 0);
  374     if (rc < 0) {
  375         rc = -errno;
  376         FIP_LOG_ERR(errno, "%s:sendmsg error\n", __func__);
  377     } else {
  378         FIP_LOG_DBG("%s: sent %d bytes to ifindex %d\n",
  379                 __func__, rc, ifindex);
  380     }
  381     return rc;
  382 }
  383 
  384 /**
  385  * fip_recv - receive from a FIP packet socket
  386  * @s: packet socket with data ready to be received
  387  * @fn: FIP receive callback to process the payload
  388  * @arg: argument to pass through to @fn
  389  */
  390 int fip_recv(int s, fip_handler *fn, void *arg)
  391 {
  392     char buf[4096];
  393     struct sockaddr_ll sa;
  394     struct iovec iov[] = {
  395         { .iov_base = buf, .iov_len = sizeof(buf), },
  396     };
  397     struct msghdr msg = {
  398         .msg_name = &sa,
  399         .msg_namelen = sizeof(sa),
  400         .msg_iov = iov,
  401         .msg_iovlen = ARRAY_SIZE(iov),
  402     };
  403     struct fiphdr *fh;
  404     size_t len, desc_len;
  405     int rc;
  406     struct ethhdr *eth = (struct ethhdr *)buf;
  407 
  408     rc = recvmsg(s, &msg, MSG_DONTWAIT);
  409     if (rc < 0) {
  410         FIP_LOG_ERRNO("packet socket recv error");
  411         return rc;
  412     }
  413 
  414     len = rc;
  415     if (len < sizeof(*fh)) {
  416         FIP_LOG_ERR(EINVAL, "received packed smaller that FIP header");
  417         return -1;
  418     }
  419 
  420     if (eth->h_proto == htons(ETH_P_8021Q))
  421         fh = (struct fiphdr *) (buf + sizeof(struct ethhdr) + VLAN_HLEN);
  422     else
  423         fh = (struct fiphdr *) (buf + sizeof(struct ethhdr));
  424 
  425     desc_len = ntohs(fh->fip_desc_len);
  426     if (len < (sizeof(*fh) + (desc_len << 2))) {
  427         FIP_LOG_ERR(EINVAL, "received data less that FIP descriptor");
  428         return -1;
  429     }
  430 
  431     if (fn)
  432         return fn(fh, &sa, arg);
  433     return 0;
  434 }