struct ndmsg
{
__u8 ndm_family;
__u8 ndm_pad1;
__u16 ndm_pad2;
__s32 ndm_ifindex; // 网络设备索引
__u16 ndm_state; // 邻居项状态
__u8 ndm_flags;
__u8 ndm_type;
};
可携带的属性如下:
enum
{
NDA_UNSPEC,
NDA_DST, // 指定邻居项的key,对于ARP就是IP地址
NDA_LLADDR, // L2
NDA_CACHEINFO,
NDA_PROBES,
__NDA_MAX
};
以ip(8)命令为例,邻居项相关的常用命令格式如下。
ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ] [ nud STATE ] | proxy ADDR } \
[ dev DEV ] [ router ] [ extern_learn ]
ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ] [ vrf NAME ]
ip neigh get ADDR dev DEV
STATE := { permanent | noarp | stale | reachable | none | incomplete | delay | probe | failed }
static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct ndmsg *ndm;
struct nlattr *tb[NDA_MAX+1];
struct neigh_table *tbl;
struct net_device *dev = NULL;
int err;
// 解析Netlink消息,将属性保存在tb数组中
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
if (err < 0)
goto out;
err = -EINVAL;
if (tb[NDA_DST] == NULL) // 必须指定NDA_DST属性
goto out;
ndm = nlmsg_data(nlh);
if (ndm->ndm_ifindex) {
// 查找网络设备并校验配置的L2层地址长度
dev = dev_get_by_index(net, ndm->ndm_ifindex);
if (dev == NULL) {
err = -ENODEV;
goto out;
}
if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len)
goto out_dev_put;
}
// 遍历所有的邻居协议链表,根据协议族找到应该处理该添加命令的邻居协议对象
read_lock(&neigh_tbl_lock);
for (tbl = neigh_tables; tbl; tbl = tbl->next) {
int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE;
struct neighbour *neigh;
void *dst, *lladdr;
if (tbl->family != ndm->ndm_family) // 比较协议族
continue;
read_unlock(&neigh_tbl_lock);
// 找到了邻居协议,下面执行添加操作
// 校验DST属性,key的长度不足为非法配置参数
if (nla_len(tb[NDA_DST]) < tbl->key_len)
goto out_dev_put;
dst = nla_data(tb[NDA_DST]); // 保存dst信息
// 解析二层地址
lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
if (ndm->ndm_flags & NTF_PROXY) {
struct pneigh_entry *pn;
err = -ENOBUFS;
pn = pneigh_lookup(tbl, net, dst, dev, 1);
if (pn) {
pn->flags = ndm->ndm_flags;
err = 0;
}
goto out_dev_put;
}
// 必须指定网络设备
if (dev == NULL)
goto out_dev_put;
// 根据key(dst)和网络设备对象,从邻居表中查找邻居项
neigh = neigh_lookup(tbl, dst, dev);
if (neigh == NULL) { // 系统中没有该邻居项,尝试创建它
// 新建场景,需要指定CREATE标记
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
err = -ENOENT;
goto out_dev_put;
}
// 该接口会查询邻居项,如果没有会创建一个返回
neigh = __neigh_lookup_errno(tbl, dst, dev);
if (IS_ERR(neigh)) {
err = PTR_ERR(neigh);
goto out_dev_put;
}
} else { // 系统中已有该邻居项,尝试更新它
// 更新场景,不能设置EXCL标记
if (nlh->nlmsg_flags & NLM_F_EXCL) {
err = -EEXIST;
neigh_release(neigh);
goto out_dev_put;
}
// 决定了能够覆盖已有邻居项
if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
flags &= ~NEIGH_UPDATE_F_OVERRIDE;
}
// 更新邻居项
err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
neigh_release(neigh);
goto out_dev_put;
}
read_unlock(&neigh_tbl_lock);
err = -EAFNOSUPPORT;
out_dev_put:
if (dev)
dev_put(dev);
out:
return err;
}
static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct ndmsg *ndm;
struct nlattr *dst_attr;
struct neigh_table *tbl;
struct net_device *dev = NULL;
int err = -EINVAL;
if (nlmsg_len(nlh) < sizeof(*ndm))
goto out;
// 找到dst,即key
dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST);
if (dst_attr == NULL)
goto out;
ndm = nlmsg_data(nlh);
if (ndm->ndm_ifindex) { // 解析网络设备索引,并找到网络设备对象
dev = dev_get_by_index(net, ndm->ndm_ifindex);
if (dev == NULL) {
err = -ENODEV;
goto out;
}
}
// 根据key和网络设备对象找到邻居协议
read_lock(&neigh_tbl_lock);
for (tbl = neigh_tables; tbl; tbl = tbl->next) {
struct neighbour *neigh;
if (tbl->family != ndm->ndm_family)
continue;
read_unlock(&neigh_tbl_lock);
if (nla_len(dst_attr) < tbl->key_len)
goto out_dev_put;
if (ndm->ndm_flags & NTF_PROXY) {
err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
goto out_dev_put;
}
if (dev == NULL)
goto out_dev_put;
// 找到要删除的邻居项
neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
if (neigh == NULL) {
err = -ENOENT;
goto out_dev_put;
}
// 删除邻居项
err = neigh_update(neigh, NULL, NUD_FAILED,
NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN);
neigh_release(neigh);
goto out_dev_put;
}
read_unlock(&neigh_tbl_lock);
err = -EAFNOSUPPORT;
out_dev_put:
if (dev)
dev_put(dev);
out:
return err;
}
static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct neigh_table *tbl;
struct ndtmsg *ndtmsg;
struct nlattr *tb[NDTA_MAX+1];
int err;
err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
nl_neightbl_policy);
if (err < 0)
goto errout;
if (tb[NDTA_NAME] == NULL) {
err = -EINVAL;
goto errout;
}
ndtmsg = nlmsg_data(nlh);
read_lock(&neigh_tbl_lock);
for (tbl = neigh_tables; tbl; tbl = tbl->next) {
if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family)
continue;
if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0)
break;
}
if (tbl == NULL) {
err = -ENOENT;
goto errout_locked;
}
/*
* We acquire tbl->lock to be nice to the periodic timers and
* make sure they always see a consistent set of values.
*/
write_lock_bh(&tbl->lock);
if (tb[NDTA_PARMS]) {
struct nlattr *tbp[NDTPA_MAX+1];
struct neigh_parms *p;
int i, ifindex = 0;
err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS],
nl_ntbl_parm_policy);
if (err < 0)
goto errout_tbl_lock;
if (tbp[NDTPA_IFINDEX])
ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
p = lookup_neigh_params(tbl, net, ifindex);
if (p == NULL) {
err = -ENOENT;
goto errout_tbl_lock;
}
for (i = 1; i <= NDTPA_MAX; i++) {
if (tbp[i] == NULL)
continue;
switch (i) {
case NDTPA_QUEUE_LEN:
p->queue_len = nla_get_u32(tbp[i]);
break;
case NDTPA_PROXY_QLEN:
p->proxy_qlen = nla_get_u32(tbp[i]);
break;
case NDTPA_APP_PROBES:
p->app_probes = nla_get_u32(tbp[i]);
break;
case NDTPA_UCAST_PROBES:
p->ucast_probes = nla_get_u32(tbp[i]);
break;
case NDTPA_MCAST_PROBES:
p->mcast_probes = nla_get_u32(tbp[i]);
break;
case NDTPA_BASE_REACHABLE_TIME:
p->base_reachable_time = nla_get_msecs(tbp[i]);
break;
case NDTPA_GC_STALETIME:
p->gc_staletime = nla_get_msecs(tbp[i]);
break;
case NDTPA_DELAY_PROBE_TIME:
p->delay_probe_time = nla_get_msecs(tbp[i]);
break;
case NDTPA_RETRANS_TIME:
p->retrans_time = nla_get_msecs(tbp[i]);
break;
case NDTPA_ANYCAST_DELAY:
p->anycast_delay = nla_get_msecs(tbp[i]);
break;
case NDTPA_PROXY_DELAY:
p->proxy_delay = nla_get_msecs(tbp[i]);
break;
case NDTPA_LOCKTIME:
p->locktime = nla_get_msecs(tbp[i]);
break;
}
}
}
if (tb[NDTA_THRESH1])
tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]);
if (tb[NDTA_THRESH2])
tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]);
if (tb[NDTA_THRESH3])
tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]);
if (tb[NDTA_GC_INTERVAL])
tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]);
err = 0;
errout_tbl_lock:
write_unlock_bh(&tbl->lock);
errout_locked:
read_unlock(&neigh_tbl_lock);
errout:
return err;
}
因篇幅问题不能全部显示,请点此查看更多更全内容