OVS 派OFPT_PORT_STATUS 流程
阅读原文时间:2023年07月13日阅读:1

依据openflow合约[OFP1.0-38],当从物理端口ovs datapath 添加,改动或者删除的时候。都会先运行详细动作。然后通过ofp_port_status异步消息告知Controller,比方当我们运行 ovs-vsctl add-port br0 eth0 之类的命令后,就会更新ovsdb数据库。某一个轮询时全局变量 reconfiguring
变为true,从而会又一次配置这个ovs。

if (reconfiguring) {
// cfg 条目能够追踪到ovsdb中某个配置发生改变
if (cfg) {
if (!reconf_txn) {
reconf_txn = ovsdb_idl_txn_create(idl);
}
// 又一次配置每一个cfg,核心入口
if (bridge_reconfigure_continue(cfg)) {
ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
}
} else {
bridge_reconfigure_continue(&null_cfg);
}
}

接下来详细运行配置:

static bool
bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
{
struct sockaddr_in *managers;
int sflow_bridge_number;
size_t n_managers;
struct bridge *br;
bool done;

assert(reconfiguring);  
// reconfigure首先要做的就是先删除旧端口,而后依据配置构建新端口  
done = bridge\_reconfigure\_ofp();

/\* Complete the configuration. \*/  
sflow\_bridge\_number = 0;  
collect\_in\_band\_managers(ovs\_cfg, &managers, &n\_managers);  
HMAP\_FOR\_EACH (br, node, &all\_bridges) {  
    struct port \*port;

    /\* We need the datapath ID early to allow LACP ports to use it as the  
     \* default system ID. \*/  
    bridge\_configure\_datapath\_id(br);

    HMAP\_FOR\_EACH (port, hmap\_node, &br->ports) {  
        struct iface \*iface;

        port\_configure(port);

        LIST\_FOR\_EACH (iface, port\_elem, &port->ifaces) {  
            iface\_configure\_cfm(iface);  
            iface\_configure\_qos(iface, port->cfg->qos);  
            iface\_set\_mac(iface);  
        }  
    }  
    bridge\_configure\_mirrors(br);  
    bridge\_configure\_flow\_eviction\_threshold(br);  
    bridge\_configure\_forward\_bpdu(br);  
    bridge\_configure\_mac\_idle\_time(br);  
    bridge\_configure\_remotes(br, managers, n\_managers);  
    bridge\_configure\_netflow(br);  
    bridge\_configure\_sflow(br, &sflow\_bridge\_number);  
    bridge\_configure\_stp(br);  
    bridge\_configure\_tables(br);  
}  
free(managers);

if (done) {  
    /\* ovs-vswitchd has completed initialization, so allow the process that  
     \* forked us to exit successfully. \*/  
    daemonize\_complete();  
    reconfiguring = false;

    VLOG\_INFO("%s (Open vSwitch) %s", program\_name, VERSION);  
}

return done;  

}

这里先删除全部的port,再加入:

static bool
bridge_reconfigure_ofp(void)
{
long long int deadline;
struct bridge *br;

time\_refresh();  
deadline = time\_msec() + OFP\_PORT\_ACTION\_WINDOW;

/\* The kernel will reject any attempt to add a given port to a datapath if  
 \* that port already belongs to a different datapath, so we must do all  
 \* port deletions before any port additions. \*/  
HMAP\_FOR\_EACH (br, node, &all\_bridges) {  
    struct ofpp\_garbage \*garbage, \*next;

    LIST\_FOR\_EACH\_SAFE (garbage, next, list\_node, &br->ofpp\_garbage) {  
        /\* It's a bit dangerous to call bridge\_run\_fast() here as ofproto's  
         \* internal datastructures may not be consistent.  Eventually, when  
         \* port additions and deletions are cheaper, these calls should be  
         \* removed. \*/  
        bridge\_run\_fast();  
        ofproto\_port\_del(br->ofproto, garbage->ofp\_port);  
        list\_remove(&garbage->list\_node);  
        free(garbage);

        time\_refresh();  
        if (time\_msec() >= deadline) {  
            return false;  
        }  
        bridge\_run\_fast();  
    }  
}

HMAP\_FOR\_EACH (br, node, &all\_bridges) {  
    struct if\_cfg \*if\_cfg, \*next;

    HMAP\_FOR\_EACH\_SAFE (if\_cfg, next, hmap\_node, &br->if\_cfg\_todo) {  
        //这里是核心,在我们的ovs bridge上添加一个接口  
        iface\_create(br, if\_cfg, -1);  
        time\_refresh();  
        if (time\_msec() >= deadline) {  
            return false;  
        }  
    }  
}

return true;  

}

依据配置 if_cfg 给br添加一个interface,假设指定的openflowport号是负数。 则表示自己主动分配:

static bool
iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
{
const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
const struct ovsrec_port *port_cfg = if_cfg->parent;

struct netdev \*netdev;  
struct iface \*iface;  
struct port \*port;  
int error;

/\* Get rid of 'if\_cfg' itself.  We already copied out the interesting  
 \* bits. \*/  
hmap\_remove(&br->if\_cfg\_todo, &if\_cfg->hmap\_node);  
free(if\_cfg);

/\* Do the bits that can fail up front.  
 \*  
 \* It's a bit dangerous to call bridge\_run\_fast() here as ofproto's  
 \* internal datastructures may not be consistent.  Eventually, when port  
 \* additions and deletions are cheaper, these calls should be removed. \*/  
bridge\_run\_fast();  
assert(!iface\_lookup(br, iface\_cfg->name));  
error = iface\_do\_create(br, iface\_cfg, port\_cfg, &ofp\_port, &netdev);  
bridge\_run\_fast();  
if (error) {  
    iface\_clear\_db\_record(iface\_cfg);  
    return false;  
}

/\* Get or create the port structure. \*/  
port = port\_lookup(br, port\_cfg->name);  
if (!port) {  
    port = port\_create(br, port\_cfg);  
}

/\* Create the iface structure. \*/  
iface = xzalloc(sizeof \*iface);  
list\_push\_back(&port->ifaces, &iface->port\_elem);  
hmap\_insert(&br->iface\_by\_name, &iface->name\_node,  
            hash\_string(iface\_cfg->name, 0));  
iface->port = port;  
iface->name = xstrdup(iface\_cfg->name);  
iface->ofp\_port = -1;  
iface->netdev = netdev;  
iface->type = iface\_get\_type(iface\_cfg, br->cfg);  
iface->cfg = iface\_cfg;

iface\_set\_ofp\_port(iface, ofp\_port);

/\* Populate initial status in database. \*/  
iface\_refresh\_stats(iface);  
iface\_refresh\_status(iface);

/\* Add bond fake iface if necessary. \*/  
if (port\_is\_bond\_fake\_iface(port)) {  
    struct ofproto\_port ofproto\_port;

    if (ofproto\_port\_query\_by\_name(br->ofproto, port->name,  
                                   &ofproto\_port)) {  
        struct netdev \*netdev;  
        int error;

        error = netdev\_open(port->name, "internal", &netdev);  
        if (!error) {  
            // 将这个网络设备增加到我们的openflow switch中  
            ofproto\_port\_add(br->ofproto, netdev, NULL);  
            netdev\_close(netdev);  
        } else {  
            VLOG\_WARN("could not open network device %s (%s)",  
                      port->name, strerror(error));  
        }  
    } else {  
        /\* Already exists, nothing to do. \*/  
        ofproto\_port\_destroy(&ofproto\_port);  
    }  
}

return true;  

}

调用ofproto(openflow sw接口)详细实现的port_add方法:

int
ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
uint16_t *ofp_portp)
{
uint16_t ofp_port;
int error;

error = ofproto->ofproto\_class->port\_add(ofproto, netdev, &ofp\_port);  
// 看 dpif\_linux\_class 的详细实现  
if (!error) {  
    // 更新我们的openflow交换机(即ofproto)  
    update\_port(ofproto, netdev\_get\_name(netdev));  
}  
if (ofp\_portp) {  
    \*ofp\_portp = error ? OFPP\_NONE : ofp\_port;  
}  
return error;  

}

static void
update_port(struct ofproto *ofproto, const char *name)
{
struct ofproto_port ofproto_port;
struct ofputil_phy_port pp;
struct netdev *netdev;
struct ofport *port;

COVERAGE\_INC(ofproto\_update\_port);

/\* Fetch 'name''s location and properties from the datapath. \*/  
netdev = (!ofproto\_port\_query\_by\_name(ofproto, name, &ofproto\_port)  
          ? ofport\_open(ofproto, &ofproto\_port, &pp)  
          : NULL);  
if (netdev) {  
    port = ofproto\_get\_port(ofproto, ofproto\_port.ofp\_port);  
    if (port && !strcmp(netdev\_get\_name(port->netdev), name)) {  
        struct netdev \*old\_netdev = port->netdev;

        /\* 'name' hasn't changed location.  Any properties changed? \*/  
        if (!ofport\_equal(&port->pp, &pp)) {  
            ofport\_modified(port, &pp);  
        }

        update\_mtu(ofproto, port);

        /\* Install the newly opened netdev in case it has changed.  
         \* Don't close the old netdev yet in case port\_modified has to  
         \* remove a retained reference to it.\*/  
        port->netdev = netdev;  
        port->change\_seq = netdev\_change\_seq(netdev);

        if (port->ofproto->ofproto\_class->port\_modified) {  
            port->ofproto->ofproto\_class->port\_modified(port);  
        }

        netdev\_close(old\_netdev);  
    } else {  
        /\* If 'port' is nonnull then its name differs from 'name' and thus  
         \* we should delete it.  If we think there's a port named 'name'  
         \* then its port number must be wrong now so delete it too. \*/  
        if (port) {  
            ofport\_remove(port);  
        }  
        ofport\_remove\_with\_name(ofproto, name);  
        // 看这里  
        ofport\_install(ofproto, netdev, &pp);  
    }  
} else {  
    /\* Any port named 'name' is gone now. \*/  
    ofport\_remove\_with\_name(ofproto, name);  
}  
ofproto\_port\_destroy(&ofproto\_port);  

}

static void
ofport_install(struct ofproto *p,
struct netdev *netdev, const struct ofputil_phy_port *pp)
{
const char *netdev_name = netdev_get_name(netdev);
struct ofport *ofport;
int error;

/\* Create ofport. \*/  
ofport = p->ofproto\_class->port\_alloc();  
if (!ofport) {  
    error = ENOMEM;  
    goto error;  
}  
ofport->ofproto = p;  
ofport->netdev = netdev;  
ofport->change\_seq = netdev\_change\_seq(netdev);  
ofport->pp = \*pp;  
ofport->ofp\_port = pp->port\_no;

/\* Add port to 'p'. \*/  
hmap\_insert(&p->ports, &ofport->hmap\_node, hash\_int(ofport->ofp\_port, 0));  
shash\_add(&p->port\_by\_name, netdev\_name, ofport);

update\_mtu(p, ofport);

/\* Let the ofproto\_class initialize its private data. \*/  
error = p->ofproto\_class->port\_construct(ofport);  
if (error) {  
    goto error;  
}  
// 更新操作完毕后。发送通知消息到Controller  
connmgr\_send\_port\_status(p->connmgr, pp, OFPPR\_ADD);  
return;

error:
VLOG_WARN_RL(&rl, "%s: could not add port %s (%s)",
p->name, netdev_name, strerror(error));
if (ofport) {
ofport_destroy__(ofport);
} else {
netdev_close(netdev);
}
}

发送port_status 和端口改变原因到SDN Controller:

void
connmgr_send_port_status(struct connmgr *mgr,
const struct ofputil_phy_port *pp, uint8_t reason)
{
/* XXX Should limit the number of queued port status change messages. */
struct ofputil_port_status ps;
struct ofconn *ofconn;

ps.reason = reason;  
ps.desc = \*pp;  
LIST\_FOR\_EACH (ofconn, node, &mgr->all\_conns) {  
    if (ofconn\_receives\_async\_msg(ofconn, OAM\_PORT\_STATUS, reason)) {  
        struct ofpbuf \*msg;

        msg = ofputil\_encode\_port\_status(&ps, ofconn->protocol);  
        ofconn\_send(ofconn, msg, NULL);  
    }  
}  

}

版权声明:本文博主原创文章。博客,未经同意不得转载。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章