最近碰到一个问题,想给br-lan同时配置dhcp和静态,配置如下:
1 2 3 4 5 6 7 8 9 10 11 | config interface 'lan' option ifname 'eth0 eth1' option force_link '1' option type 'bridge' option proto 'dhcp' config alias lan_alias option interface 'lan' option proto 'static' option ipaddr '192.168.100.1' option netmask '255.255.255.0' |
有问题,静态只有在dhcp拨上号之后才会生效。反过来则是可以的。netifd的源码以前也阅读过,但是实在过于复杂,这次准备再次阅读。
输出调试信息:
./netfid -d -1 -l 100 -S
-d是设置调试的掩码,-1 打开所有调试类型。
-l是调试等级,设置为一个很大数,开启为debug模式。
-S 输出到stderr。
某些初始化函数是在main之前完成的。netifd使用了
#define __init __attribute__((constructor))
netifd使用avl树来存储proto handler,device, interface等。
注册静态协议:
proto-static.c
__init static_proto_init()
{
add_proto_handler(&static_proto);
}
main() -> proto-shell.c proto_shell_init()
打开/lib/netifd/proto目录,popen执行下面的所有的脚本,读取脚本的输出结果,每个脚本对应一个proto。脚本会产生json数据,包含了proto的名字,所有的uci配置等。解析后调用add_proto_handler(proto)来添加添加proto。
可以手动查看 脚本的输出:
# cd /lib/netifd/proto
# ./dhcp.sh '' dump
{ "name": "dhcp", "config": [ [ "ipaddr:ipaddr", 3 ], [ "hostname:hostname", 3 ], [ "clientid", 3 ], [ "vendorid", 3 ], [ "broadcast:bool", 7 ], [ "norelease:bool", 7 ], [ "reqopts:list(string)", 3 ], [ "defaultreqopts:bool", 7 ], [ "iface6rd", 3 ], [ "sendopts:list(string)", 1 ], [ "delegate", 7 ], [ "zone6rd", 3 ], [ "zone", 3 ], [ "mtu6rd", 3 ], [ "customroutes", 3 ], [ "classlessroute", 7 ] ], "no-device": false, "no-proto-task": false, "available": false, "renew-handler": true, "lasterror": false, "teardown-on-l3-link-down": false }
device type定义了设备类型。指定了设备所有的参数、已经创建的方法等。设备类型在main之前的初始化函数中注册。
bridge.c
__init bridge_device_type_init(void)
{
device_type_add(&bridge_device_type);
}
device.c
__init simple_device_type_init(void)
{
device_type_add(&simple_device_type);
}
vlandev.c
__init vlandev_device_type_init(void)
{
device_type_add(&vlan8021q_device_type);
}
还有一些其它用的不多的设备类型。
bridge device type结构体如下:
1 2 3 4 5 6 7 8 9 10 11 12 | static struct device_type bridge_device_type = { .name = "bridge" , .config_params = &bridge_attr_list, .bridge_capability = true , .name_prefix = "br" , .create = bridge_create, .config_init = bridge_config_init, .vlan_update = bridge_dev_vlan_update, .reload = bridge_reload, . free = bridge_free, .dump_info = bridge_dump_info, }; |
name是device type的名字。
config_params是一个数组,定义了这个设备类型对应的uci配置类型。
create是创建设备的接口。
config_init是解析设备配置参数,比如bridge类型解析brport。
config_params定义了这种类型的设备有哪些uci配置参数:
bridge type的参数:
static const struct blobmsg_policy bridge_attrs[__BRIDGE_ATTR_MAX] = {
[BRIDGE_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY },
[BRIDGE_ATTR_STP] = { "stp", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_FORWARD_DELAY] = { "forward_delay", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_PRIORITY] = { "priority", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_AGEING_TIME] = { "ageing_time", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_HELLO_TIME] = { "hello_time", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_MAX_AGE] = { "max_age", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_IGMP_SNOOP] = { "igmp_snooping", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_BRIDGE_EMPTY] = { "bridge_empty", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_MULTICAST_QUERIER] = { "multicast_querier", BLOBMSG_TYPE_BOOL },
[BRIDGE_ATTR_HASH_MAX] = { "hash_max", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_ROBUSTNESS] = { "robustness", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_QUERY_INTERVAL] = { "query_interval", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_LAST_MEMBER_INTERVAL] = { "last_member_interval", BLOBMSG_TYPE_INT32 },
[BRIDGE_ATTR_VLAN_FILTERING] = { "vlan_filtering", BLOBMSG_TYPE_BOOL },
};
比较重要的参数如上,ports表示这个桥有哪些成员,bridge_empty,表示允许没有成员。
simple device 的参数:
static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = {
[DEV_ATTR_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
[DEV_ATTR_MTU] = { .name = "mtu", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_MTU6] = { .name = "mtu6", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_MACADDR] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
[DEV_ATTR_TXQUEUELEN] = { .name = "txqueuelen", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_IPV6] = { .name = "ipv6", .type = BLOBMSG_TYPE_BOOL },
[] = { .name = "ip6segmentrouting", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_PROMISC] = { .name = "promisc", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_RPFILTER] = { .name = "rpfilter", .type = BLOBMSG_TYPE_STRING },
[DEV_ATTR_ACCEPTLOCAL] = { .name = "acceptlocal", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_IGMPVERSION] = { .name = "igmpversion", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_MLDVERSION] = { .name = "mldversion", .type = BLOBMSG_TYPE_INT32 },
[] = { .name = "neighreachabletime", .type = BLOBMSG_TYPE_INT32 },
[] = { .name = "neighgcstaletime", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_DADTRANSMITS] = { .name = "dadtransmits", .type = BLOBMSG_TYPE_INT32 },
[] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL },
[] = { .name = "multicast_router", .type = BLOBMSG_TYPE_INT32 },
[] = { .name = "multicast_fast_leave", . type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_MULTICAST] = { .name ="multicast", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_LEARNING] = { .name ="learning", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_UNICAST_FLOOD] = { .name ="unicast_flood", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_SENDREDIRECTS] = { .name = "sendredirects", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_NEIGHLOCKTIME] = { .name = "neighlocktime", .type = BLOBMSG_TYPE_INT32 },
[DEV_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
[] = { .name = "drop_v4_unicast_in_l2_multicast", .type = BLOBMSG_TYPE_BOOL },
[] = { .name = "drop_v6_unicast_in_l2_multicast", .type = BLOBMSG_TYPE_BOOL },
[] = { .name = "drop_gratuitous_arp", .type = BLOBMSG_TYPE_BOOL },
[] = { .name = "drop_unsolicited_na", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_ARP_ACCEPT] = { .name = "arp_accept", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_AUTH] = { .name = "auth", .type = BLOBMSG_TYPE_BOOL },
};
device的结构体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | struct device { struct device_type *type; // device所属的类型 struct avl_node avl; // 所有的device存储在avl tree中,这个本devie的avl node struct safe_list users; // device的所有用户 struct safe_list aliases; struct vlist_tree vlans; struct kvlist vlan_aliases; char ifname[IFNAMSIZ + 1]; struct blob_attr *config; bool config_pending; bool sys_present; /* DEV_EVENT_ADD */ bool present; /* DEV_EVENT_UP */ int active; /* DEV_EVENT_LINK_UP */ bool link_active; bool auth_status; bool external; bool disabled; bool deferred; bool hidden; bool current_config; bool iface_config; bool default_config; bool wireless; bool wireless_ap; bool wireless_isolate; struct interface *config_iface; /* set interface up or down */ device_state_cb set_state; const struct device_hotplug_ops *hotplug_ops; struct device_user parent; struct device_settings orig_settings;
|
这里字段很多。
parent
是父device,主要是vlan的parent。如eth0是eth0.1的parent。当eth0.1状态发生变化时,会调用
device_claim/device_release,来增减对父device的引用计数,并更改父device的状态。
active
一个用户claim,就+1,一个用户release,就-1。
config_pending
是否延后配置,控制是否执行 device_init_pending()。
设备的用户,struct devce_user
1 2 3 4 5 6 7 8 9 | struct device_user { struct safe_list list; bool claimed; bool hotplug; bool alias; uint8_t ev_idx[__DEV_EVENT_MAX]; struct device *dev; void (*cb)( struct device_user *, enum device_event); }; |
claimed,是否执行了device_claim(),执行一次设置为true,再次执行,检查为true,立即返回。
执行device_release(),设置为false。
device解析和初始化流程
有些device是在配置文件里面直接定义的,如:
config device 'wan_dev'
option name 'eth1'
option mtu '1500'
option keepup '1'
设备初始化,就是读取network中device类型的这些device。
初始化是在 config.c config_init_all()中执行的。
int config_init_all(void)
{
config_init = true;
config_init_devices(true);
config_init_vlans();
config_init_devices(false);
config_init_interfaces();
config_init = false;
device_init_pending();
interface_start_pending();
}
device的初始化config_init_devices(),执行了两次,一次先初始化桥接口。接着在初始化非桥接口。
config_init_devices(bool bridge) 初始化流程:
1 遍历network的所有section,如果section type不是device则跳过。
2 读取name选项,没有则跳过。name作为device的名字。
3 读取type,如果有type,则使用device_type_get(type),得到devicetype。
4 获得设备的param list,如果有devtype,则使用devtype->config_params。如果没有devtype,
或者devtype的config_params为空,则使用simple_device_type.config_params;
5 如果是桥设备,则调用config_fixup_bridge_ports()把当前这个device section的ifname选项重命名为
ports。新的uci应该使用
list ports 'eth0'
list ports 'eth1'
这种配置,为了兼容老的代码,支持ifname。
6 将根据params,读取uci中的所有参数,并存储到blob buf中。uci_to_blob(&b, s, params);
7 如果有devtype,调用dev = device_create(name, devtype, b.head);创建设备。
b.head是上一步中读取到的blob配置。
8 如果没有devtype,调用dev = device_get(name, 1); 创建设备,
设置 dev->current_config = true;
调用 device_apply_config(dev, dev->type, b.head); 应用配置。
9 设置dev->default_config为false。
device_init_pending(void)
遍历所有的device,如果其dev->config_pending为true。则执行:
dev->type->config_init(dev)
dev->config_pending = false
device_check_state(dev)
device_create()
参数
const char *name, struct device_type *type, struct blob_attr *config
1 查找 odev = device_find(name);
2 如果odev找到,则
odev->current_config = true;
change = device_apply_config(odev, type, config);
如果change是DEV_CONFIG_RECREATE,则调用device_delete(odev);删除旧设备,代码继续往下走。
如果不是,则直接返回旧设备。
3 复制一下config, config = blob_memdup(config);
4 调用devtype的创建函数创建设备dev = type->create(name, type, config);
5 如果 !config_init && dev->config_pending,则执行 type->config_init(dev);
然后将 dev->config_pending = false;
config_init 是 config.c的一个全局变量,在config_init_devices()和config_init_interfaces()期间为false。
config_pending表示device还未配置,是挂起状态。在桥设备的创建函数中,被设置为true。
6 调用 device_check_state(dev);
device_get()
参数
const char *name, int create
1 avl查找设备 dev = avl_find_element(&devices, name, dev, avl);
2 如果name[0] == '@',则返回 device_alias_get(name + 1);
3 如果找到设备,且create > 1,且 !dev->external,则
system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
dev->external = true;
device_set_present(dev, true);
create > 1 是ubus等命令创建设备时,指定,此时设备的dev->external设置为true。
4 找到设备,返回dev
5 没找到设备,且没有create,返回空。
6 如果有create,则调用 device_create_default(name, create > 1);
device_apply_config()
参数:struct device *dev, struct device_type *type, struct blob_attr *attr
1 调用change = device_set_config(dev, type, config);
将config设置到dev->settings中去。
a. 如果存在,则返回 dev->type->reload(dev, attr)
b. uci_blob_check_equal(dev->config, attr, cfg) 如果相同,则返回 DEV_CONFIG_NO_CHANGE
c. 如果是普通设备,则,调用device_init_settings(dev, tb);
d. 否则返回DEV_CONFIG_RECREATE
2 如果change为DEV_CONFIG_RESTART
执行down up动作。
device_check_state()
参数:struct device *dev
1 如果不存在dev->type->check_state,则执行simple_device_type.check_state(dev);
2 否则,执行 dev->type->check_state(dev);
这个函数是检查接口的状态,然后拉起接口。因此这个接口是触发事件的。
device_add_user()
参数 struct device_user *dep, struct device *dev
将dep添加到dev,作为dev的用户。
1 首先设置dep->dev = dev
2 如果dep->alias,则将dep添加到dev的alias链表。head = &dev->aliases;
3 否则,添加到普通用户链表。
3 如果 dep->cb存在,且dev->present,则
因为添加用户后,可能dev可能已经ready,不会产生事件,所以需要初始化调用一下cb。
a. 执行 dep->cb(dep, DEV_EVENT_ADD)
b. 如果dev->active。则执行:dep->cb(dep, DEV_EVENT_UP);
c. 如果dev->link_active,则:dep->cb(dep, DEV_EVENT_LINK_UP);
device_claim()
参数 struct device_user *dep
1 已经claim了,立即返回, if (dep->claimed) return 0
2 设置 dep->claimed = true;
3 device->active 增1,如果大于1,表明已经active过了,则立即返回
4 广播setup事件,device_broadcast_event(dev, DEV_EVENT_SETUP);
5 设置默认参数,当前只有mac参数,device_fill_default_settings(dev);
6 如果是非external设备,则执行 ret = dev->set_state(dev, true);
7 广播UP事件, device_broadcast_event(dev, DEV_EVENT_UP);
device_release()
参数 struct device_user *dep
是device_claim的反向操作。
1 如果dep->claim为0,则立即返回,
2 设置dep->claim为false。
3 device->active 减1,如果没有减到0,则返回。
4 广播TEARDOWN事件
5 如果非external设备,执行dev->set_state(dev, false);
6 如果dev->active为0,广播 DOWN事件,device_broadcast_event(dev, DEV_EVENT_DOWN);
simple_device_set_state()
参数 struct device *dev, bool state
1 读取parent device,pdev = dev->parent.dev; 如果pdev不为空。
如果state为up,则 device_claim(&dev->parent);
如果state为0,则 device_release(&dev->parent);
2 调用set_device_state(dev, state)
system_if_get_settings()
参数:struct device *dev, struct device_settings *s
从内核读取接口的参数,到s,并设置flags。如:
if (ioctl(sock_ioctl, SIOCGIFMTU, &ifr) == 0) {
s->mtu = ifr.ifr_mtu;
s->flags |= DEV_OPT_MTU;
}
system_if_apply_settings
参数:struct device *dev, struct device_settings *s, unsigned int apply_mask
设置apply_maks中指定的参数,参数的值存储在s中。设置到dev中去。如:
if (apply_mask & DEV_OPT_MTU) {
ifr.ifr_mtu = s->mtu;
ioctl(sock_ioctl, SIOCSIFMTU, &ifr)
}
set_device_state()
参数 struct device *dev, bool state
a. 如果state是up,
a.1 拿取ifindex, device_set_ifindex(dev, system_if_resolve(dev)); 如果不存在则退出
a.2 读取旧的设置:system_if_get_settings(dev, &dev->orig_settings);
保存旧的flags:dev->orig_settings.valid_flags = dev->orig_settings.flags;
旧的flags与要设置的flags与,保存到旧的flag。这样后续恢复的时候,只要恢复这些被修改的设置。
dev->orig_settings.flags &= dev->settings.flags;
a.3 system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
a.4 拉起接口:system_if_up(dev);
b. 如果state是0
b.1 system_if_down(dev); down掉接口
b.2 应用旧配置:
system_if_apply_settings(dev, &dev->orig_settings, dev->orig_settings.flags);
device_broadcast_cb()
参数 void *ctx, struct safe_list *list
1 计算得到 struct device_user *dep
2 执行 dep->cb(dep, *ev)
device_broadcast_event()
参数 struct device *dev, enum device_event ev
1 遍历dev->users,执行 device_broadcast_cb。
2 编译dev->aliases,执行 device_broadcast_cb
device_set_ifname()
参数:struct device *dev, const char *name
这里不仅支持设置,也支持修改。
1 如果和原来的相同,直接返回,if (!strcmp(dev->ifname, name)) return 0
2 如果有dev->avl.key,则表明插入过了,先删除。avl_delete(&devices, &dev->avl);
3 拷贝到ifname,strcpy(dev->ifname, name);
4 如果有dev-avl.key,则再次插入:ret = avl_insert(&devices, &dev->avl);
5 广播事件:device_broadcast_event(dev, DEV_EVENT_UPDATE_IFNAME);
类似的:
device_set_ifindex(),设置ifindex,并广播DEV_EVENT_UPDATE_IFINDEX事件。
device_set_link(),设置link_active,并广播 DEV_EVENT_LINK_UP、DOWN事件。
device_set_present(),设置sys_present。
device_init_virtual()
参数 struct device *dev, struct device_type *type, const char *name
1 初始化users和aliases链表
INIT_SAFE_LIST(&dev->users);
INIT_SAFE_LIST(&dev->aliases);
2 设置type
dev->type = type;
3 如果name为非空,则调用 device_set_ifname(dev, name) 设置name到dev->ifname
4 如果dev->set_state为空
dev->set_state = set_device_state
system_if_clear_state()
参数:struct device *dev
清除桥成员,down掉device。
1 调用device_set_ifindex(),设置设备的ifindex。
2 如果dev->external || !dev->ifindex,则返回。
这表明external的设备,其状态不会变。
3 将其down掉:system_if_flags(dev->ifname, 0, IFF_UP),移除IFF_UP标志。
4 如果是桥,则删除桥。
5 如果不是桥,但是是某个桥的成员,则将其从桥中移除。
6 调用system_if_clear_entries(),删除这个设备的ROUTE,ADDR等。
device_init()
参数:struct device *dev, struct device_type *type, const char *ifname
1 执行 device_init_virtual(dev, type, ifname)
2 设置 avl key, dev->avl.key = dev->ifname;
3 插入到avl,ret = avl_insert(&devices, &dev->avl);
4 执行,system_if_clear_state(dev);
device_create_default()
参数 const char *name, bool external
1 分配一个dev,dev = calloc(1, sizeof(*dev));
2 设置 dev->external = external;
dev->set_state = simple_device_set_state;
3 初始化device, device_init(dev, &simple_device_type, name)
4 设置 dev->default_config = true;
5 执行 device_check_state(dev);
6 返回dev
alias设备是alias的interface。当一个interface up的时候,另外一个interface可以使用这个interface的l3设备拨号。比如:
interface lan
option proto pppoe
option ifname eth0
interface lan2
option ifname @lan
option proto static
这样,在lan拨上号之后,lan2就可以用ppp接口来设置静态地址。在interface.c的interface_event()函数中调用 alias_notify_device(iface->name, adev) 来通知alias device相关事件。
alias.c,alias设备存储在 aliases avl 树中:static struct avl_tree aliases;
结构体
struct alias_device {
struct avl_node avl;
struct device dev;
struct device_user dep;
struct device_user new_dep;
bool update;
char name[];
};
它也有个dev,嵌入在alias_device结构体里面。
alias device的创建入口是 device_get() 判断 name的第一个字符为 @,调用device_alias_get()。
struct device * device_alias_get(const char *name)
从aliases里面,查找,如果找到,则返回 &alias->dev。
否则,调用 alias_device_create(name, &alias_device_type, NULL) 创建设备。
static struct device *
alias_device_create(const char *name, struct device_type *devtype, struct blob_attr *attr)
1 分配alias结构体,拷贝name到alias->name,
2 device初始化
alias->dev.set_state = alias_device_set_state
alias->dev.hidden = true
device_init_virtual(&alias->dev, devtype, NULL)
这里device的名字为NULL,也不会加入到正常的device avl树中。
3 将alias插入到aliases avl树中。
4 设置alias->dep
alias->dep.alias = true;
alias->dep.cb = alias_device_cb;
5 调用device_check_state(&alias->dev);
alias devtype
static struct device_type alias_device_type = {
.name = "Network alias",
.create = alias_device_create,
.free = alias_device_free,
.check_state = alias_check_state,
};
这里的check_state回调,会在alias device device_check_state() 的时候执行。alias devtype也是一种device type。是不是可以在uci里面直接定义一个alias device。
int alias_check_state(struct device *dev)
这函数的目的,是检查alias对应的interface的l3是否up。如果up,则调用alias_set_device(alias, dev)。
设置alias的真实dev。这个函数是主动检查。而alias_notify_device()是被动通知。
1 得到alias
alias = container_of(dev, struct alias_device, dev);
2 查找interface
iface = vlist_find(&interfaces, alias->name, iface, node);
如果找到,且iface up,则,获得l3 device。
if (iface && iface->state == IFS_UP)
ndev = iface->l3_dev.dev;
3 设置l3给alias,alias_set_device(alias, ndev);
void alias_notify_device(const char *name, struct device *dev)
这是interface down up等的时候,调用这个接口,通知alias设备。alias会在alias avl树中找出alias,
然后将dev设置进去。
1 查找alias:
alias = avl_find_element(&aliases, name, alias, avl);
2 如果找到,则
alias_set_device(alias, dev);
void alias_set_device(struct alias_device *alias, struct device *dev)
将dev设置到 device_add_user(&alias->dep, dev),这样就会调用cb。来更新alias->dev。
然后设置
device_set_ifindex(&alias->dev, dev->ifindex);
device_set_ifname(&alias->dev, dev->ifname);
注意其实alias需要维护新旧两个device user。因为某些拨号方式,前后接口名称是变化的。alias需要
处理这种情况,可能旧的还未释放,新的又来了。
int alias_device_set_state(struct device *dev, bool state)
set_state()通常由device_claim()调用,表示使用者用还是不用这个device。如果用,通常就拉起device。
如果不用,就down掉device。alias的set_state,将这个值传递给l3 device。
1 获取alias:
alias = container_of(dev, struct alias_device, dev);
2 如果alias->dep.dev为空,则表明l3 device还没有拉起来。直接返回
3 如果state为true,则调用 device_claim(&alias->dep),并返回。
4 如果state为false,则调用 device_release(&alias->dep) 释放。
如果alias->update为真,表明 l3 device 发生了变化。
调用 alias_set_device(alias, alias->new_dep.dev),设置新的dev。
void alias_device_cb(struct device_user *dep, enum device_event ev)
这个是alias->dep->cb,在alias_set_device时,调用device_add_user()会调用这个函数。
因此这个函数主要是设置alias->dev的状态。当l3起来的时候,把这个alias dev拉起,然后
在广播到这个alias dev的用户。
1 获得alias
alias = container_of(dep, struct alias_device, dep);
2 根据事件类型,设置dev的状态:
DEV_EVENT_ADD:device_set_present(&alias->dev, true);
DEV_EVENT_REMOVE:device_set_present(&alias->dev, false);
DEV_EVENT_LINK_UP:device_set_link(&alias->dev, true);
DEV_EVENT_LINK_DOWN:device_set_link(&alias->dev, false);
DEV_EVENT_UPDATE_IFINDEX:device_set_ifindex(&alias->dev, dep->dev->ifindex);
其它事件,直接广播:device_broadcast_event(&alias->dev, ev);
interface是3层接口,可拨号,配置ip,路由。有两种类型接口,interface和alias。
接口初始化:
config_init_interfaces(void)
遍历interface类型的section,执行 config_parse_interface(s, false);
遍历alias类型的section,执行 config_parse_interface(s, true)
interface数据结构
struct interface {
struct vlist_node node;
struct list_head hotplug_list;
enum interface_event hotplug_ev;
const char *name;
const char *device;
char *jail;
char *jail_ifname;
int netns_fd;
bool available;
bool autostart;
bool config_autostart;
bool device_config;
bool enabled;
bool link_state;
bool force_link;
bool dynamic;
bool policy_rules_set;
bool link_up_event;
time_t start_time;
enum interface_state state;
enum interface_config_state config_state;
enum interface_update_flags updated;
struct list_head users;
/* for alias interface */
const char *parent_ifname;
struct interface_user parent_iface;
/* main interface that the interface is bound to */
struct device_user main_dev;
struct device_user ext_dev;
/* interface that layer 3 communication will go through */
struct device_user l3_dev;
struct blob_attr *config;
/* primary protocol state */
const struct proto_handler *proto_handler;
struct interface_proto_state *proto;
struct interface_ip_settings proto_ip;
struct interface_ip_settings config_ip;
struct vlist_tree host_routes;
struct vlist_tree host_neighbors;
int metric;
int dns_metric;
unsigned int ip4table;
unsigned int ip6table;
/* IPv6 assignment parameters */
enum interface_id_selection_type assignment_iface_id_selection;
struct in6_addr assignment_fixed_iface_id;
uint8_t assignment_length;
int32_t assignment_hint;
struct list_head assignment_classes;
int assignment_weight;
/* errors/warnings while trying to bring up the interface */
struct list_head errors;
/* extra data provided by protocol handlers or modules */
struct avl_tree data;
struct uloop_timeout remove_timer;
struct ubus_object ubus;
};
接口的用户:
struct interface_user {
struct list_head list;
struct interface *iface;
void (*cb)(struct interface_user *dep, struct interface *iface, enum interface_event ev);
};
接口的uci属性:
static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
[IFACE_ATTR_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_AUTO] = { .name = "auto", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_JAIL_IFNAME] = { .name = "jail_ifname", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DEFAULTROUTE] = { .name = "defaultroute", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_PEERDNS] = { .name = "peerdns", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_METRIC] = { .name = "metric", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DNS_METRIC] = { .name = "dns_metric", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP6ASSIGN] = { .name = "ip6assign", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP4TABLE] = { .name = "ip4table", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP6TABLE] = { .name = "ip6table", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_IP6CLASS] = { .name = "ip6class", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_IP6IFACEID] = { .name = "ip6ifaceid", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_FORCE_LINK] = { .name = "force_link", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_IP6WEIGHT] = { .name = "ip6weight", .type = BLOBMSG_TYPE_INT32 },
};
解析、创建、添加接口:
void config_parse_interface(struct uci_section *s, bool alias)
1 如果不是 alias接口,读取type,并得到 devtype = device_type_get(type)
2 如果devtype是桥类型,则调用 config_parse_bridge_interface(s, devtype)。
这个接口构造创建device的参数,并调用device_create(name, devtype, b.head),创建桥设备。
a. 计算桥接口的名字 br-lan,并以name添加到blob,创建device都需要name。
b. config_fixup_bridge_ports,将旧的ifname option转换为port。
c. 调用 uci_to_blob(&b, s, devtype->config_params),将section中的参数,转换为blob_buf。
d. 调用 device_create(name, devtype, b.head) 创建桥设备。
e. 重新初始化blob_buf,将桥设备的接口名,已ifname参数写入blob_buf。这样继续解析interface
的时候,有了ifname为br-lan。
3 读取接口配置参数:uci_to_blob(&b, s, &interface_attr_list) 到blob_buf。注意这里的接口属性,
入、如上面的iface_attrs数组。包括device,proto等。不包括ipv4地址ipaddr等。ipv4是proto属性。
4 创建接口:iface = interface_alloc(s->e.name, b.head, false);
6 如果有proto,则读取proto相关的uci参数到blob_buff
uci_to_blob(&b, s, iface->proto_handler->config_params)
7 如果不是桥,则读取simple device的uci参数到blob_buff。
8 复制一下配置:config = blob_memdup(b.head);
9 如果是alias interface,调用 interface_add_alias(iface, config) 添加。
10 否则,调用 interface_add(iface, config)添加。
创建接口:
struct interface * interface_alloc(const char *name, struct blob_attr *config, bool dynamic)
这个函数分配iface,然后初始化相关成员,以及根据uci参数,初始化相关成员。
1 分配iface
初始化几个list,iface->errors,iface->users 等。
2 interface_ip_init(iface),初始化iface的iface->proto_ip,iface->config_ip,iface->host_routes
config_ip是uci配置中的ip参数,如dns。
proto_ip是拨号协议的ip参数。
3 初始化3个device user的cb
iface->main_dev.cb = interface_main_dev_cb
iface->l3_dev.cb = interface_l3_dev_cb
iface->ext_dev.cb = interface_ext_dev_cb
4 根据uci参数实话,autostart, force_link等成员。
5 proto_attach_interface(iface, proto_name),将proto_handler赋给 iface->proto_handler。
6 返回iface。
添加 interface
bool __interface_add(struct interface *iface, struct blob_attr *config, bool alias)
这个函数,将iface加入到interfaces 链表中。还有一个重要的功能。解析接口。
如果是alias interface。读取uci option "interface" 到 iface->parent_ifname,如果没有则返回false。
如果不是alias interface,则读取uci option "device",如果没有,则读取ifname。到 iface->device。
初始化拉起接口
interface_start_pending(void)
遍历所有的接口,如果iface->autostart为true,则执行 interface_set_up(iface)。启动接口。
autostart为false的接口,只能通过ubus拉起了。
interface update 机制
在初始化interfaces vlist_tree的时候,指定了一个update接口函数。这个是vlist tree的一个机制。
在插入、更新、删除 成员的时候,都会掉这个函数。
vlist_init(&interfaces, avl_strcmp, interface_update)
interface_update()
参数:struct vlist_tree *tree, struct vlist_node *node_new, struct vlist_node *node_old
如果是新增interface,则执行下面的动作:
interface_event(if_new, IFEV_CREATE)
proto_init_interface(if_new, if_new->config)
interface_claim_device(if_new)
netifd_ubus_add_interface(if_new), 给这个interface添加ubus object。如network.interface.lan
interface_claim_device(struct interface *iface)
这是一个重要的函数,它执行interface的最后初始化动作。将interface、alias interface、device的关系建立起来。
1 如果有iface->parent_ifname,则是一个alias interface,则:
在interface中查找parent。设置 iface->parent_iface.cb 为 interface_alias_cb。
调用interface_add_user()然后将iface->parent_iface设置为parent的users。
在interface_event()中,将遍历所有users,执行cb。也就是说interface_alias_cb()会被调用。
2 如果iface->device非空,且协议不支持NODEV,则
dev = device_get(iface->device, true) 创建或获得一个device。
如果dev存在,将其设置为main dev。interface_set_main_dev(iface, dev)
接口事件通知:
interface_event(struct interface *iface, enum interface_event ev)
接口发生某个事件后,调用这个函数,来通知接口的用户,以及接口的l3 device的alias用户。
1 遍历iface->users,执行 dep->cb(dep, iface, ev)
2 遍历 iface_all_users,执行 dep->cb(dep, iface, ev);
3 调用 alias_notify_device(iface->name, adev),通知alias device。
void interface_set_proto_state(struct interface *iface, struct interface_proto_state *state)
将interface_proto_state设置到interface。这个函数主要是设置变量。
iface->state = IFS_DOWN
iface->proto = state
state->proto_event = interface_proto_event_cb
state->iface = iface
最重要的是指定了proto event的回调函数为interface_proto_event_cb,在拨上号等事件的时候
会调用这个函数。
interface_set_up(struct interface *iface)
在config_init_all()中:
config_init_interfaces()-》config_parse_interface(),负责解析、创建、添加接口。
interface_start_pending()-》interface_set_up()负责拉起所有的接口,使其开始拨号、配置。
void
interface_proto_event_cb(struct interface_proto_state *state, enum interface_proto_event ev)
proto拨上号等事件,interface的处理函数。
proto是拨号协议,用来实现DHCP, PPPoE等拨号。它主要有两个结构体。
struct proto_handler {
struct avl_node avl;
unsigned int flags;
const char *name;
const struct uci_blob_param_list *config_params;
struct interface_proto_state *(*attach)(const struct proto_handler *h,
struct interface *iface, struct blob_attr *attr);
};
proto_handler是具体的协议。每个协议都会注册一个proto_handler。在proto_shell_add_handler()函数会添加所有的shell协议,除了static外,所有的proto都是动态协议。attach 被设置为:proto->attach = proto_shell_attach。
struct interface_proto_state {
const struct proto_handler *handler;
struct interface *iface;
/* filled in by the protocol user */
void (*proto_event)(struct interface_proto_state *, enum interface_proto_event ev);
/* filled in by the protocol handler */
int (*notify)(struct interface_proto_state *, struct blob_attr *data);
int (*cb)(struct interface_proto_state *, enum interface_proto_cmd cmd, bool force);
void (*free)(struct interface_proto_state *);
};
interface_proto_state 是一个拨号实例,由proto_handler的attach函数动态创建,附着在iface上。
对于shell proto:notify 设置为 proto_shell_notify。cb 设置为 proto_shell_handler。
proto_event是由interface层设置的,给proto层调用的回调函数,当proto层发生事件的时候,调用这个函数通知interface。调用过程:
proto_shell_notify -》 proto_shell_update_link -》proto_event(&state->proto, IFPEV_UP)
notify是由拨号script通过ubus发送拨号结果,调用过程:
.name = "notify_proto", .handler = netifd_iface_notify_proto
netifd_iface_notify_proto -》 iface->proto->notify(iface->proto, msg)
cb是由interface调用,给proto发命令,用于控制proto的up、down,调用过程:
interface_proto_event ->proto->cb(proto, cmd, force)
proto.c 接口
======================
proto_attach_interface(struct interface *iface, const char *proto_name)
就是找到proto_name对应的proto_handler,赋给 iface->proto_handler,如果没有,则把
no_proto赋给iface->proto_handler。
proto_init_interface(struct interface *iface, struct blob_attr *attr)
在interface update中被调用。
执行proto->attach(proto, iface, attr),分配 struct interface_proto_state *state,
然后调用 interface_set_proto_state(iface, state)。这个函数设置proto的interface回调proto_event。
interface_proto_event()
interface调用这个接口,来调用proto的cb。
主要是执行:ret = proto->cb(proto, cmd, force)
proto-shell.c 接口
===========================
struct proto_shell_handler {
struct list_head list;
struct proto_handler proto;
char *config_buf;
char *script_name;
bool init_available;
struct uci_blob_param_list config;
};
proto_handler结构体是内嵌在上述结构体中的,上述结构体还包含了proto_handler额外所需要的信息。
这些信息是通用proto层不需要的。
struct proto_shell_state {
struct interface_proto_state proto;
struct proto_shell_handler *handler;
struct blob_attr *config;
struct uloop_timeout teardown_timeout;
/*
* Teardown and setup interface again if it is still not up (IFS_UP)
* after checkup_interval seconds since previous attempt. This check
* will be disabled when the config option "checkup_interval" is
* missing or has a negative value
*/
int checkup_interval;
struct uloop_timeout checkup_timeout;
struct netifd_process script_task;
struct netifd_process proto_task;
enum proto_shell_sm sm;
bool proto_task_killed;
bool renew_pending;
int last_error;
struct list_head deps;
};
interface_proto_state 结构体是内嵌在上述结构体中的。它还包含了拨号进程的信息等信息。
有两个进程信息:script_task 和 proto_task。
前者是 netifd 调用 拨号脚本,拨号脚本的进程信息存在script_task。
拨号脚本再给 netifd发ubus消息,由netifd 创建拨号进程。拨号进程存在proto_task。
netifd始终只发起script_task的启动和杀死。而拨号程序总是有脚本发ubus消息给netifd。
netifd按命令,启动和杀死拨号程序。
proto_shell_attach()
shell proto的attach()函数。分配struct proto_shell_state。初始化成员,最后返回interface_proto_state
1 struct proto_shell_state *state = calloc(1, sizeof(*state))
state->proto.free = proto_shell_free;
state->proto.notify = proto_shell_notify;
state->proto.cb = proto_shell_handler;
state->teardown_timeout.cb = proto_shell_teardown_timeout_cb;
state->script_task.cb = proto_shell_script_cb;
state->script_task.dir_fd = proto_fd;
state->script_task.log_prefix = iface->name;
state->proto_task.cb = proto_shell_task_cb;
state->proto_task.dir_fd = proto_fd;
state->proto_task.log_prefix = iface->name;
state->handler = container_of(h, struct proto_shell_handler, proto);
proto_shell_handler()
执行interface的命令,开始拨号,或结束拨号。这个函数的主要任务就是启动、停止拨号程序。
proto_shell_state结构体的script_task成员,存储了拨号程序的信息。
还有一个小的状态机,state->sm。其值有 S_IDLE/S_SETUP/S_SETUP_ABORT/S_TEARDOWN。
拨号命令:PROTO_CMD_SETUP/PROTO_CMD_TEARDOWN/PROTO_CMD_RENEW。
proto_shell_task_finish()
脚本和拨号程序退出的回调proto_shell_script_cb、proto_shell_script_cb会调用这个函数进行处理。
如果是异常退出会产生一些事件,比如:
state->proto.proto_event(&state->proto, IFPEV_LINK_LOST)
proto_shell_notify ()
处理ubus消息。根据ACTION的不同,执行不同的动作。最重要的是 0/1/2
0:处理拨号结果,调用 proto_shell_update_link
1:启动拨号程序,调用 proto_shell_run_command
2:杀死拨号程序,调用 proto_shell_kill_command
proto_shell_update_link()
处理ubus发过来的拨号结果:
1 读取是 up = blobmsg_get_bool(tb[NOTIFY_LINK_UP])
如果 没有up,则直接给interface一个LINK_LOST事件:
state->proto.proto_event(&state->proto, IFPEV_LINK_LOST),并返回。
2 如果传递了NOTIFY_IFNAME,则更新l3 device:
interface_set_l3_dev(iface, dev)
device_claim(&iface->l3_dev)
device_set_present(dev, true)
3 proto_apply_ip_settings(iface, data, addr_ext)
interface_update_complete(state->proto.iface)
4 state->proto.proto_event(&state->proto, IFPEV_UP)
1 拨号脚本调用ubus notify_proto 方法。执行 ubus.c netifd_iface_notify_proto()
2 在 netifd_iface_notify_proto(),首先得到 struct interface *iface。然后调用
iface->proto->notify(iface->proto, msg)
3 notify接口是在 proto-shell.c proto_shell_attach()的时候注册的
state->proto.notify = proto_shell_notify。
4 proto_shell_attach()是proto的attach()接口。每个proto都有attach()接口。
proto->attach()是在proto_init_interface()中执行的。后者又是在interface_update()中执行的。
5 proto_shell_notify()
根据拨号脚本传递过来的ACTION参数。执行对应的命令。
其中命令0,是拨上号的命令,执行proto_shell_update_link()
6 proto_shell_update_link()