/* * lib/genl/ctrl.c Generic Netlink Controller * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * Copyright (c) 2003-2008 Thomas Graf */ /** * @ingroup genl_mngt * @defgroup ctrl Controller * @brief * * @{ */ #include #include #include #include #include #include #include /** @cond SKIP */ #define CTRL_VERSION 0x0001 static struct nl_cache_ops genl_ctrl_ops; /** @endcond */ static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h) { return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, NLM_F_DUMP); } static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, .maxlen = GENL_NAMSIZ }, [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, }; static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = { [CTRL_ATTR_OP_ID] = { .type = NLA_U32 }, [CTRL_ATTR_OP_FLAGS] = { .type = NLA_U32 }, }; static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, struct genl_info *info, void *arg) { struct genl_family *family; struct nl_parser_param *pp = arg; int err; family = genl_family_alloc(); if (family == NULL) { err = -NLE_NOMEM; goto errout; } if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) { err = -NLE_MISSING_ATTR; goto errout; } if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) { err = -NLE_MISSING_ATTR; goto errout; } family->ce_msgtype = info->nlh->nlmsg_type; genl_family_set_id(family, nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID])); genl_family_set_name(family, nla_get_string(info->attrs[CTRL_ATTR_FAMILY_NAME])); if (info->attrs[CTRL_ATTR_VERSION]) { uint32_t version = nla_get_u32(info->attrs[CTRL_ATTR_VERSION]); genl_family_set_version(family, version); } if (info->attrs[CTRL_ATTR_HDRSIZE]) { uint32_t hdrsize = nla_get_u32(info->attrs[CTRL_ATTR_HDRSIZE]); genl_family_set_hdrsize(family, hdrsize); } if (info->attrs[CTRL_ATTR_MAXATTR]) { uint32_t maxattr = nla_get_u32(info->attrs[CTRL_ATTR_MAXATTR]); genl_family_set_maxattr(family, maxattr); } if (info->attrs[CTRL_ATTR_OPS]) { struct nlattr *nla, *nla_ops; int remaining; nla_ops = info->attrs[CTRL_ATTR_OPS]; nla_for_each_nested(nla, nla_ops, remaining) { struct nlattr *tb[CTRL_ATTR_OP_MAX+1]; int flags = 0, id; err = nla_parse_nested(tb, CTRL_ATTR_OP_MAX, nla, family_op_policy); if (err < 0) goto errout; if (tb[CTRL_ATTR_OP_ID] == NULL) { err = -NLE_MISSING_ATTR; goto errout; } id = nla_get_u32(tb[CTRL_ATTR_OP_ID]); if (tb[CTRL_ATTR_OP_FLAGS]) flags = nla_get_u32(tb[CTRL_ATTR_OP_FLAGS]); err = genl_family_add_op(family, id, flags); if (err < 0) goto errout; } } err = pp->pp_cb((struct nl_object *) family, pp); errout: genl_family_put(family); return err; } /** * @name Cache Management * @{ */ int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result) { return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result); } /** * Look up generic netlink family by id in the provided cache. * @arg cache Generic netlink family cache. * @arg id Family identifier. * * Searches through the cache looking for a registered family * matching the specified identifier. The caller will own a * reference on the returned object which needs to be given * back after usage using genl_family_put(). * * @return Generic netlink family object or NULL if no match was found. */ struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id) { struct genl_family *fam; if (cache->c_ops != &genl_ctrl_ops) BUG(); nl_list_for_each_entry(fam, &cache->c_items, ce_list) { if (fam->gf_id == id) { nl_object_get((struct nl_object *) fam); return fam; } } return NULL; } /** * @name Resolver * @{ */ /** * Look up generic netlink family by family name in the provided cache. * @arg cache Generic netlink family cache. * @arg name Family name. * * Searches through the cache looking for a registered family * matching the specified name. The caller will own a reference * on the returned object which needs to be given back after * usage using genl_family_put(). * * @return Generic netlink family object or NULL if no match was found. */ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, const char *name) { struct genl_family *fam; if (cache->c_ops != &genl_ctrl_ops) BUG(); nl_list_for_each_entry(fam, &cache->c_items, ce_list) { if (!strcmp(name, fam->gf_name)) { nl_object_get((struct nl_object *) fam); return fam; } } return NULL; } /** @} */ /** * Resolve generic netlink family name to its identifier * @arg sk Netlink socket. * @arg name Name of generic netlink family * * Resolves the generic netlink family name to its identifer and returns * it. * * @return A positive identifier or a negative error code. */ int genl_ctrl_resolve(struct nl_sock *sk, const char *name) { struct nl_cache *cache; struct genl_family *family; int err; if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0) return err; family = genl_ctrl_search_by_name(cache, name); if (family == NULL) { err = -NLE_OBJ_NOTFOUND; goto errout; } err = genl_family_get_id(family); genl_family_put(family); errout: nl_cache_free(cache); return err; } /** @} */ static struct genl_cmd genl_cmds[] = { { .c_id = CTRL_CMD_NEWFAMILY, .c_name = "NEWFAMILY" , .c_maxattr = CTRL_ATTR_MAX, .c_attr_policy = ctrl_policy, .c_msg_parser = ctrl_msg_parser, }, { .c_id = CTRL_CMD_DELFAMILY, .c_name = "DELFAMILY" , }, { .c_id = CTRL_CMD_GETFAMILY, .c_name = "GETFAMILY" , }, { .c_id = CTRL_CMD_NEWOPS, .c_name = "NEWOPS" , }, { .c_id = CTRL_CMD_DELOPS, .c_name = "DELOPS" , }, }; static struct genl_ops genl_ops = { .o_cmds = genl_cmds, .o_ncmds = ARRAY_SIZE(genl_cmds), }; /** @cond SKIP */ extern struct nl_object_ops genl_family_ops; /** @endcond */ static struct nl_cache_ops genl_ctrl_ops = { .co_name = "genl/family", .co_hdrsize = GENL_HDRSIZE(0), .co_msgtypes = GENL_FAMILY(GENL_ID_CTRL, "nlctrl"), .co_genl = &genl_ops, .co_protocol = NETLINK_GENERIC, .co_request_update = ctrl_request_update, .co_obj_ops = &genl_family_ops, }; static void __init ctrl_init(void) { genl_register(&genl_ctrl_ops); } static void __exit ctrl_exit(void) { genl_unregister(&genl_ctrl_ops); } /** @} */ a> 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
import random

import channel
import controller
from messages import *

class NetifControllerFactory(controller.ControllerFactory):
    """Factory for creating network interface controllers.
    Also handles the 'back-end' channel to the device driver domain.
    """

    def __init__(self):
        controller.ControllerFactory.__init__(self)

        self.majorTypes = [ CMSG_NETIF_BE ]

        self.subTypes = {
            CMSG_NETIF_BE_CREATE : self.recv_be_create,
            CMSG_NETIF_BE_CONNECT: self.recv_be_connect,
            CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
            }
        self.attached = 1
        self.registerChannel()

    def createInstance(self, dom):
        """Create or find the network interface controller for a domain.
        """
        #print 'netif>createInstance> dom=', dom
        netif = self.getInstanceByDom(dom)
        if netif is None:
            netif = NetifController(self, dom)
            self.addInstance(netif)
        return netif

    def getDomainDevices(self, dom):
        netif = self.getInstanceByDom(dom)
        return (netif and netif.getDevices()) or []

    def getDomainDevice(self, dom, vif):
        netif = self.getInstanceByDom(dom)
        return (netif and netif.getDevice(vif)) or None
        
    def setControlDomain(self, dom):
        """Set the 'back-end' device driver domain.
        """
        if self.dom == dom: return
        self.deregisterChannel()
        self.attached = 0
        self.dom = dom
        self.registerChannel()
        #
        #if xend.netif.be_port.remote_dom != 0:
        #    xend.netif.recovery = True
        #    xend.netif.be_port = xend.main.port_from_dom(dom)
        #

    def getControlDomain(self):
        return self.dom

    def recv_be_create(self, msg, req):
        self.callDeferred(0)
    
    def recv_be_connect(self, msg, req):
        val = unpackMsg('netif_be_connect_t', msg)
        dom = val['domid']
        vif = val['netif_handle']
        netif = self.getInstanceByDom(dom)
        if netif:
            netif.send_interface_connected(vif)
        else:
            print "recv_be_connect> unknown vif=", vif
            pass

    def recv_be_driver_status_changed(self, msg, req):
        val = unpackMsg('netif_be_driver_status_changed_t', msg)
        status = val['status']
        if status == NETIF_DRIVER_STATUS_UP and not self.attached:
            # If we are not attached the driver domain was changed, and
            # this signals the new driver domain is ready.
            for netif in self.getInstances():
                netif.reattach_devices()
            self.attached = 1

##         pl = msg.get_payload()
##         status = pl['status']
##         if status == NETIF_DRIVER_STATUS_UP:
##             if xend.netif.recovery:
##                 print "New netif backend now UP, notifying guests:"
##                 for netif_key in interface.list.keys():
##                     netif = interface.list[netif_key]
##                     netif.create()
##                     print "  Notifying %d" % netif.dom
##                     msg = xend.utils.message(
##                         CMSG_NETIF_FE,
##                         CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0,
##                         { 'handle' : 0, 'status' : 1 })
##                     netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg)
##                 print "Done notifying guests"
##                 recovery = False
                
class NetDev:
    """Info record for a network device.
    """

    def __init__(self, vif, mac):
        self.vif = vif
        self.mac = mac
        self.evtchn = None

    def sxpr(self):
        vif = str(self.vif)
        mac = ':'.join(map(lambda x: "%x" % x, self.mac))
        return ['netif', ['vif', vif], ['mac', mac]]
    
class NetifController(controller.Controller):
    """Network interface controller. Handles all network devices for a domain.
    """
    
    def __init__(self, factory, dom):
        #print 'NetifController> dom=', dom
        controller.Controller.__init__(self, factory, dom)
        self.devices = {}
        
        self.majorTypes = [ CMSG_NETIF_FE ]

        self.subTypes = {
            CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
                self.recv_fe_driver_status_changed,
            CMSG_NETIF_FE_INTERFACE_CONNECT    :
                self.recv_fe_interface_connect,
            }
        self.registerChannel()
        #print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx


    def randomMAC(self):
        # VIFs get a random MAC address with a "special" vendor id.
        # 
        # NB. The vendor is currently an "obsolete" one that used to belong
        # to DEC (AA-00-00). Using it is probably a bit rude :-)
        # 
        # NB2. The first bit of the first random octet is set to zero for
        # all dynamic MAC addresses. This may allow us to manually specify
        # MAC addresses for some VIFs with no fear of clashes.
        mac = [ 0xaa, 0x00, 0x00,
                random.randint(0x00, 0x7f),
                random.randint(0x00, 0xff),
                random.randint(0x00, 0xff) ]
        return mac

    def lostChannel(self):
        print 'NetifController>lostChannel>', 'dom=', self.dom
        #for vif in self.devices:
        #    self.send_be_destroy(vif)
        controller.Controller.lostChannel(self)

    def getDevices(self):
        return self.devices.values()

    def getDevice(self, vif):
        return self.devices.get(vif)

    def addDevice(self, vif, vmac):
        if vmac is None:
            mac = self.randomMAC()
        else:
            mac = [ int(x, 16) for x in vmac.split(':') ]
        if len(mac) != 6: raise ValueError("invalid mac")
        #print "attach_device>", "vif=", vif, "mac=", mac
        dev = NetDev(vif, mac)
        self.devices[vif] = dev
        return dev

    def attach_device(self, vif, vmac):
        """Attach a network device.
        If vmac is None a random mac address is assigned.

        @param vif interface index
        @param vmac mac address (string)
        """
        self.addDevice(vif, vmac)
        d = self.factory.addDeferred()
        self.send_be_create(vif)
        return d

    def reattach_devices(self):
        """Reattach all devices when the back-end control domain has changed.
        """
        d = self.factory.addDeferred()
        self.send_be_create(vif)
        self.attach_fe_devices(0)

    def attach_fe_devices(self):
        for dev in self.devices.values():
            msg = packMsg('netif_fe_interface_status_changed_t',
                          { 'handle' : dev.vif,
                            'status' : NETIF_INTERFACE_STATUS_DISCONNECTED,
                            'evtchn' : 0,
                            'mac'    : dev.mac })
            self.writeRequest(msg)
    
    def recv_fe_driver_status_changed(self, msg, req):
        if not req: return
        msg = packMsg('netif_fe_driver_status_changed_t',
                      { 'status'        : NETIF_DRIVER_STATUS_UP,
                        'nr_interfaces' : len(self.devices) })
        self.writeRequest(msg)
        self.attach_fe_devices()

    def recv_fe_interface_connect(self, msg, req):
        val = unpackMsg('netif_fe_interface_connect_t', msg)
        dev = self.devices[val['handle']]
        dev.evtchn = channel.eventChannel(0, self.dom)
        msg = packMsg('netif_be_connect_t',
                      { 'domid'          : self.dom,
                        'netif_handle'   : dev.vif,
                        'evtchn'         : dev.evtchn['port1'],
                        'tx_shmem_frame' : val['tx_shmem_frame'],
                        'rx_shmem_frame' : val['rx_shmem_frame'] })
        self.factory.writeRequest(msg)

    def send_interface_connected(self, vif):
        dev = self.devices[vif]
        msg = packMsg('netif_fe_interface_status_changed_t',
                      { 'handle' : dev.vif,
                        'status' : NETIF_INTERFACE_STATUS_CONNECTED,
                        'evtchn' : dev.evtchn['port2'],
                        'mac'    : dev.mac })
        self.writeRequest(msg)

    def send_be_create(self, vif):
        dev = self.devices[vif]
        msg = packMsg('netif_be_create_t',
                      { 'domid'        : self.dom,
                        'netif_handle' : dev.vif,
                        'mac'          : dev.mac })
        self.factory.writeRequest(msg)

    def send_be_destroy(self, vif):
        print 'send_be_destroy>', 'dom=', self.dom, 'vif=', vif
        dev = self.devices[vif]
        del self.devices[vif]
        msg = packMsg('netif_be_destroy_t',
                      { 'domid'        : self.dom,
                        'netif_handle' : vif })
        self.factory.writeRequest(msg)