ovs-vswitchd的启动分析(无修改源码)
(一)主要数据结构和概念了解
1.概念
在 OVS 中, 有几个非常重要的概念:
Bridge: Bridge 代表一个以太网交换机(Switch),一个主机中可以创建一个或者多个 Bridge 设备。 Port: 端口与物理交换机的端口概念类似,每个 Port 都隶属于一个 Bridge。 Interface: 连接到 Port 的网络接口设备。在通常情况下,Port 和 Interface 是一对一的关系, 只有在配置 Port 为 bond 模式后,Port 和 Interface 是一对多的关系。
当我们创建了一个交换机(网桥)以后,此时网络功能不受影响,但是会产生一个虚拟网卡,名字就是brname,之所以会产生一个虚拟网卡,是为了实现接下来的网桥(交换机)功能。
有了这个交换机以后,我还需要为这个交换机增加端口(port),一个端口,就是一个物理网卡,当网卡加入到这个交换机之后,其工作方式就和普通交换机的一个端口的工作方式类似了。
这里要特别注意,网卡加入网桥以后,要按照网桥的工作标准工作,那么加入的一个端口就必须是以混杂模式工作,工作在链路层,处理2层的帧,所以这个port就不需要配置IP了。(你没见过哪个交换的端口有IP的吧)
那么接下来你可能会问,通常的交换机不都是有一个管理接口,可以telnet到交换机上进行配置吧,那么在OVS中创建的虚拟交换机有木有这种呢,有的!上面既然创建交换机brname的时候产生了一个虚拟网口brname,那么,你给这个虚拟网卡配置了IP以后,就相当于给交换机的管理接口配置了IP,此时一个正常的虚拟交换机就搞定了。
2.数据结构
在ofproto-provider.h中注释里是这样说的。
这里定义了四类数据结构:
Struct ofproto表示一个交换机 Struct ofport表示交换机上的一个端口 Struct rule表示交换机上的一条flow规则 Struct ofgroup表示一个flow规则组
(二)main主函数分析
在文件openvswitch-2.11.4/vswitchd/ovs-vswitchd.c中!
Openvswitch主要管理两种类型的设备,一个是创建的虚拟网桥,一个是连接到虚拟网桥上的设备。
int main(int argc, char *argv[]) { ... 线程开启、网桥初始化等等 ... while (!exiting) { memory_run(); if (memory_should_report()) { struct simap usage; simap_init(&usage); bridge_get_memory_usage(&usage); memory_report(&usage); simap_destroy(&usage); } bridge_run(); //其中bridge_run就是初始化数据库中已经创建的虚拟网桥。 unixctl_server_run(unixctl); netdev_run(); //在内核中添加虚拟网卡 memory_wait(); bridge_wait(); unixctl_server_wait(unixctl); netdev_wait(); if (exiting) { poll_immediate_wake(); } poll_block(); if (should_service_stop()) { exiting = true; } } ... 退出和销毁操作 ... return 0; }
(三)虚拟网桥的初始化bridge_run
OVS源码--openflow(四) - 程序员大本营
在文件openvswitch-2.11.4/vswitchd/bridge.c中!
bridge_run --调用-->
void bridge_run(void) { bridge_run__(); if (ovsdb_idl_get_seqno(idl) != idl_seqno || if_notifier_changed(ifnotifier)) { ... bridge_reconfigure(cfg ? cfg : &null_cfg); ... } }
分支(一):主要会用到
bridge_run__ --调用--> ofproto_run让每一个网桥去执行他对应的功能
static void bridge_run__(void) { ...... 其他操作 ...... /* Let each bridge do the work that it needs to do. */ HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_run(br->ofproto); } }
在文件openvswitch-2.11.4/ofproto/ofproto-dpif.c中!
ofproto_run --回调--> run方法
const struct ofproto_class ofproto_dpif_class = { ... run, ... port_add, ... };
int ofproto_run(struct ofproto *p) //--------------重点 { error = p->ofproto_class->run(p); ...... connmgr_run(p->connmgr, handle_openflow); return error; }
在文件openvswitch-2.11.4/ofproto/ofproto-dpif.c中!
run方法会初始化netflow, sflow, ipfix,stp, rstp, mac address learning等一系列操作。
同时也会去校验流表项规则是否过期!!!
static int run(struct ofproto *ofproto_) { stp_run(ofproto); rstp_run(ofproto); new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif)); if (ofproto->dump_seq != new_dump_seq) { struct rule *rule, *next_rule; if (ofproto->dump_seq != new_dump_seq) { struct rule *rule, *next_rule; long long now = time_msec(); /* We know stats are relatively fresh, so now is a good time to do some * periodic work. */ ofproto->dump_seq = new_dump_seq; ovs_mutex_lock(&ofproto_mutex); LIST_FOR_EACH_SAFE (rule, next_rule, expirable, &ofproto->up.expirable) { rule_expire(rule_dpif_cast(rule), now); //-------这里校验是否超时 } ovs_mutex_unlock(&ofproto_mutex); } return 0; }
分支(二):不会被用到,了解即可
bridge_reconfigure(从ovsdb-server里面读取出来的配置) --调用--> bridge_add_ports对于每一个网桥,将网卡添加进去
static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) { HMAP_FOR_EACH (br, node, &all_bridges) { bridge_add_ports(br, &br->wanted_ports); shash_destroy(&br->wanted_ports); } bridge_run__(); }
bridge_add_ports --调用--> bridge_add_ports__ --调用--> iface_create --调用--> iface_do_create --调用--> netdev_open和ofproto_port_add
int ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev, ofp_port_t *ofp_portp) { error = ofproto->ofproto_class->port_add(ofproto, netdev); }
ofproto_port_add --回调--> port_add --调用--> dpif_port_add
int dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) { error = dpif->dpif_class->port_add(dpif, netdev, &port_no); }
dpif_port_add --回调--> port_add/dpif_netdev_port_add
const struct dpif_class dpif_netdev_class = { "netdev", .... dpif_netdev_port_add, .... };
dpif_netdev_port_add --调用--> do_add_port
static int dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) { struct dp_netdev *dp = get_dp_netdev(dpif); ovs_mutex_lock(&dp->port_mutex); dpif_port = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); if (!error) { *port_nop = port_no; error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no); } ovs_mutex_unlock(&dp->port_mutex); return error; }
do_add_port这里会调用内核模块openvswitch.ko,在内核中添加虚拟网卡。
static int do_add_port(struct dp_netdev *dp, const char *devname, const char *type, odp_port_t port_no) OVS_REQUIRES(dp->port_mutex) { struct dp_netdev_port *port; int error; /* Reject devices already in 'dp'. */ if (!get_port_by_name(dp, devname, &port)) { return EEXIST; } error = port_create(devname, type, port_no, &port); if (error) { return error; } hmap_insert(&dp->ports, &port->node, hash_port_no(port_no)); seq_change(dp->port_seq); reconfigure_datapath(dp); return 0; }
(四)虚拟网卡的初始化(与设备关联)netdev_run
在文件openvswitch-2.11.4/lib/netdev.c:中!
void netdev_run(void) OVS_EXCLUDED(netdev_mutex) { netdev_initialize(); struct netdev_registered_class *rc; CMAP_FOR_EACH (rc, cmap_node, &netdev_classes) { if (rc->class->run) { rc->class->run(rc->class); } } }
依次循环调用netdev_classes中的每一个run。
对于不同类型的虚拟网卡,都有对应的netdev_class。
#define NETDEV_LINUX_CLASS_COMMON \ .run = netdev_linux_run, \ ...
NETDEV_LINUX_CLASS_COMMON
const struct netdev_class netdev_linux_class = { NETDEV_LINUX_CLASS_COMMON, LINUX_FLOW_OFFLOAD_API, .type = "system", .construct = netdev_linux_construct, .get_stats = netdev_linux_get_stats, .get_features = netdev_linux_get_features, .get_status = netdev_linux_get_status, .get_block_id = netdev_linux_get_block_id }; const struct netdev_class netdev_tap_class = { NETDEV_LINUX_CLASS_COMMON, .type = "tap", .construct = netdev_linux_construct_tap, .get_stats = netdev_tap_get_stats, .get_features = netdev_linux_get_features, .get_status = netdev_linux_get_status, }; const struct netdev_class netdev_internal_class = { NETDEV_LINUX_CLASS_COMMON, .type = "internal", .construct = netdev_linux_construct, .get_stats = netdev_internal_get_stats, .get_status = netdev_internal_get_status, };
netdev_run --调用--> netdev_linux_run 会调用netlink的sock得到虚拟网卡的状态,并且更新状态。
static void netdev_linux_run(const struct netdev_class *netdev_class OVS_UNUSED) { struct nl_sock *sock; sock = netdev_linux_notify_sock();do { ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); error = nl_sock_recv(sock, &buf, &nsid, false); if (!error) { struct rtnetlink_change change; if (rtnetlink_parse(&buf, &change)) { struct netdev *netdev_ = NULL; char dev_name[IFNAMSIZ]; if (!change.ifname) { change.ifname = if_indextoname(change.if_index, dev_name); } if (change.ifname) { netdev_ = netdev_from_name(change.ifname); } if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); ovs_mutex_lock(&netdev->mutex); netdev_linux_update(netdev, nsid, &change); ovs_mutex_unlock(&netdev->mutex); } if (change.ifname && rtnetlink_type_is_rtnlgrp_link(change.nlmsg_type)) { /* Need to try updating the LAG information. */ ovs_mutex_lock(&lag_mutex); netdev_linux_update_lag(&change); ovs_mutex_unlock(&lag_mutex); } netdev_close(netdev_); } } } while (!error); }
(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
原文链接:https://www.cnblogs.com/ssyfj/p/14767851.html