第一篇介绍了netlink消息的接收,接收到的消息是最原始的消息格式,本篇介绍如何把原始的消息,转化为upcall对象。
1、parse_odp_packet函数
static int
parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf,
struct dpif_upcall *upcall, int *dp_ifindex)
{
static const struct nl_policy ovs_packet_policy[] = {
/* Always present. */
[OVS_PACKET_ATTR_PACKET] = { .type = NL_A_UNSPEC,
.min_len = ETH_HEADER_LEN },
[OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED },
/* OVS_PACKET_CMD_ACTION only. */
[OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true },
[OVS_PACKET_ATTR_EGRESS_TUN_KEY] = { .type = NL_A_NESTED, .optional = true },
[OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
[OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = true }
};
struct ovs_header *ovs_header;
struct nlattr *a[ARRAY_SIZE(ovs_packet_policy)];
struct nlmsghdr *nlmsg;
struct genlmsghdr *genl;
struct ofpbuf b;
int type;
ofpbuf_use_const(&b, buf->data, buf->size);
nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); //获取nlmsghdr信息
genl = ofpbuf_try_pull(&b, sizeof *genl); //获取genlmsghdr信息
ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); //获取ovs_header信息
if (!nlmsg || !genl || !ovs_header
|| nlmsg->nlmsg_type != ovs_packet_family
|| !nl_policy_parse(&b, 0, ovs_packet_policy, a, //获取其他属性信息
ARRAY_SIZE(ovs_packet_policy))) {
return EINVAL;
}
type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS
: genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION
: -1);
if (type < 0) {
return EINVAL;
}
/* (Re)set ALL fields of '*upcall' on successful return. */
upcall->type = type;
upcall->key = CONST_CAST(struct nlattr *,
nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
dpif_flow_hash(&dpif->dpif, upcall->key, upcall->key_len, &upcall->ufid);
upcall->userdata = a[OVS_PACKET_ATTR_USERDATA];
upcall->out_tun_key = a[OVS_PACKET_ATTR_EGRESS_TUN_KEY];
upcall->actions = a[OVS_PACKET_ATTR_ACTIONS];
upcall->mru = a[OVS_PACKET_ATTR_MRU];
/* Allow overwriting the netlink attribute header without reallocating. */
dp_packet_use_stub(&upcall->packet, //构建upcall->packet对象,包括base指针、data指针、长度等信息
CONST_CAST(struct nlattr *,
nl_attr_get(a[OVS_PACKET_ATTR_PACKET])) - 1,
nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]) +
sizeof(struct nlattr));
dp_packet_set_data(&upcall->packet,
(char *)dp_packet_data(&upcall->packet) + sizeof(struct nlattr));
dp_packet_set_size(&upcall->packet, nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]));
*dp_ifindex = ovs_header->dp_ifindex;
return 0;
}
2、nl_policy_parse函数
/* Parses the 'msg' starting at the given 'nla_offset' as a sequence of Netlink
* attributes. 'policy[i]', for 0 <= i < n_attrs, specifies how the attribute
* with nla_type == i is parsed; a pointer to attribute i is stored in
* attrs[i]. Returns true if successful, false on failure.
*
* If the Netlink attributes in 'msg' follow a Netlink header and a Generic
* Netlink header, then 'nla_offset' should be NLMSG_HDRLEN + GENL_HDRLEN. */
bool
nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset,
const struct nl_policy policy[],
struct nlattr *attrs[], size_t n_attrs)
{
struct nlattr *nla;
size_t left;
size_t i;
memset(attrs, 0, n_attrs * sizeof *attrs);
if (msg->size < nla_offset) {
VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse");
return false;
}
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, nla_offset, 0), //获取netlink的其他属性
msg->size - nla_offset)
{
uint16_t type = nl_attr_type(nla);
if (type < n_attrs && policy[type].type != NL_A_NO_ATTR) {
const struct nl_policy *e = &policy[type];
if (!nl_attr_validate(nla, e)) {
return false;
}
if (attrs[type]) {
VLOG_DBG_RL(&rl, "duplicate attr %"PRIu16, type);
}
attrs[type] = nla;
}
}
if (left) {
VLOG_DBG_RL(&rl, "attributes followed by garbage");
return false;
}
for (i = 0; i < n_attrs; i++) { //检查必须字段是否有值
const struct nl_policy *e = &policy[i];
if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[i]) {
VLOG_DBG_RL(&rl, "required attr %"PRIuSIZE" missing", i);
return false;
}
}
return true;
}