[PATCHv2 4/4] netlink: Introduce a helper function to simplify netlink functions

Shi Lei shi_lei at massclouds.com
Thu Jan 7 02:52:57 UTC 2021


On 2021-01-06 at 18:21, Michal Privoznik wrote:
>On 1/6/21 3:40 AM, Shi Lei wrote:
>> Extract common code as helper function virNetlinkTalk, then simplify
>> the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor].
>>
>> Signed-off-by: Shi Lei <shi_lei at massclouds.com>
>> ---
>>   src/util/virnetlink.c | 225 +++++++++++++++++-------------------------
>>   src/util/virnetlink.h |   4 +-
>>   2 files changed, 94 insertions(+), 135 deletions(-)
>>
>> diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
>> index fdcb0dc0..2936a3ef 100644
>> --- a/src/util/virnetlink.c
>> +++ b/src/util/virnetlink.c
>> @@ -353,6 +353,52 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
>>       return 0;
>>   }
>>  
>> +
>> +static int
>> +virNetlinkTalk(const char *ifname,
>> +               virNetlinkMsg *nl_msg,
>> +               uint32_t src_pid,
>> +               uint32_t dst_pid,
>> +               struct nlmsghdr **resp,
>> +               unsigned int *resp_len,
>> +               int *error,
>> +               virNetlinkTalkFallback fallback)
>> +{
>> +    if (virNetlinkCommand(nl_msg, resp, resp_len,
>> +                          src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +        return -1;
>> +
>> +    if (*resp_len < NLMSG_LENGTH(0) || resp == NULL)
>
>This needs to be *resp == NULL, because now we're passing a pointer to a
>pointer. 

Uh. It's my bad. I'll fix it.

>
>> +        goto malformed_resp;
>> +
>> +    if ((*resp)->nlmsg_type == NLMSG_ERROR) {
>> +        struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(resp);
>
>And this needs to be NLMSG_DATA(*resp); 

Yes!

>Also, might be worth putting an empty line here to create two blocks:
>one with variable declaration and the other with the code. 

Okay.

>
>
>> +        if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> +            goto malformed_resp;
>> +
>> +        if (-err->error == EOPNOTSUPP && fallback)
>> +            return fallback(ifname);
>> +
>> +        if (err->error < 0) {
>> +            if (error)
>> +                *error = err->error;
>> +            else
>> +                virReportSystemError(-err->error,
>> +                                     "%s", _("netlink error"));
>
>Since this is a two line body, it should be wrapped in curly braces
>(according to our coding style) which means that both bodies should have
>them (we don't really like one body having them while the other not). 

I feel like it can be :
    if (error)
        *error = err->error;
    else
        virReportSystemError(-err->error, "%s", _("netlink error"));

Since ^this line isn't more than 80 columns.

>> +
>> +            return -1;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +
>> + malformed_resp:
>> +    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +                   _("malformed netlink response message"));
>> +    return -1;
>> +}
>> +
>> +
>>   int
>>   virNetlinkDumpCommand(struct nl_msg *nl_msg,
>>                         virNetlinkDumpCallback callback,
>> @@ -396,6 +442,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
>>       return 0;
>>   }
>>  
>> +
>>   /**
>>    * virNetlinkDumpLink:
>>    *
>> @@ -420,15 +467,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>                      void **nlData, struct nlattr **tb,
>>                      uint32_t src_pid, uint32_t dst_pid)
>>   {
>> -    int rc = -1;
>> -    struct nlmsgerr *err;
>>       struct ifinfomsg ifinfo = {
>>           .ifi_family = AF_UNSPEC,
>>           .ifi_index  = ifindex
>>       };
>> -    unsigned int recvbuflen;
>>       g_autoptr(virNetlinkMsg) nl_msg = NULL;
>>       g_autofree struct nlmsghdr *resp = NULL;
>> +    unsigned int resp_len = 0;
>> +    int error = 0;
>>  
>>       if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
>>           return -1;
>> @@ -459,46 +505,23 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
>>       }
>>   # endif
>>  
>> -    if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
>> -                          src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
>> +    if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
>> +                       &resp, &resp_len, &error, NULL) < 0) {
>> +        virReportSystemError(error,
>> +                             _("error dumping %s (%d) interface"),
>> +                             ifname, ifindex);
>>           return -1;
>> +    }
>>  
>> -    if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
>> -        goto malformed_resp;
>> -
>> -    switch (resp->nlmsg_type) {
>> -    case NLMSG_ERROR:
>> -        err = (struct nlmsgerr *)NLMSG_DATA(resp);
>> -        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> -            goto malformed_resp;
>> -
>> -        if (err->error) {
>> -            virReportSystemError(-err->error,
>> -                                 _("error dumping %s (%d) interface"),
>> -                                 ifname, ifindex);
>> -            return -1;
>> -        }
>> -        break;
>> -
>> -    case GENL_ID_CTRL:
>> -    case NLMSG_DONE:
>> -        rc = nlmsg_parse(resp, sizeof(struct ifinfomsg),
>> -                         tb, IFLA_MAX, NULL);
>> -        if (rc < 0)
>> -            goto malformed_resp;
>> -        break;
>> -
>> -    default:
>> -        goto malformed_resp;
>> +    if ((resp->nlmsg_type != GENL_ID_CTRL && resp->nlmsg_type != NLMSG_DONE) ||
>> +        nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL) < 0) {
>> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +                       _("malformed netlink response message"));
>> +        return -1;
>>       }
>>  
>>       *nlData = g_steal_pointer(&resp);
>>       return 0;
>> -
>> - malformed_resp:
>> -    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> -                   _("malformed netlink response message"));
>> -    return rc;
>>   }
>>  
>>  
>> @@ -523,13 +546,12 @@ virNetlinkNewLink(const char *ifname,
>>                     virNetlinkNewLinkDataPtr extra_args,
>>                     int *error)
>>   {
>> -    struct nlmsgerr *err;
>>       struct nlattr *linkinfo = NULL;
>>       struct nlattr *infodata = NULL;
>> -    unsigned int buflen;
>>       struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
>>       g_autoptr(virNetlinkMsg) nl_msg = NULL;
>>       g_autofree struct nlmsghdr *resp = NULL;
>> +    unsigned int resp_len = 0;
>>  
>>       *error = 0;
>>  
>> @@ -591,37 +613,17 @@ virNetlinkNewLink(const char *ifname,
>>           }
>>       }
>>  
>> -    if (virNetlinkCommand(nl_msg, &resp, &buflen, 0, 0, NETLINK_ROUTE, 0) < 0)
>> +    if (virNetlinkTalk(ifname, nl_msg, 0, 0,
>> +                       &resp, &resp_len, error, NULL) < 0)
>>           return -1;
>>  
>> -    if (buflen < NLMSG_LENGTH(0) || resp == NULL)
>> -        goto malformed_resp;
>> -
>> -    switch (resp->nlmsg_type) {
>> -    case NLMSG_ERROR:
>> -        err = (struct nlmsgerr *)NLMSG_DATA(resp);
>> -        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
>> -            goto malformed_resp;
>> -
>> -        if (err->error < 0) {
>> -            *error = err->error;
>> -            return -1;
>> -        }
>> -        break;
>> -
>> -    case NLMSG_DONE:
>> -        break;
>> -
>> -    default:
>> -        goto malformed_resp;
>> +    if (resp->nlmsg_type != NLMSG_DONE) {
>> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +                       _("malformed netlink response message"));
>> +        return -1;
>>       }
>
>This is not correct IMO. The way that control flows through this
>function before your patch is (I'm trying to create virbr0 using 'virsh
>net-start default'):
>
>1) virNetlinkCommand() suceeds,
>2) resp->nlmsg_type == NLMSG_ERROR even though the bridge was created,
>3) err->error is 0 hence we hit 'break' and fall out of the switch and ..
>
>>  
>>       return 0; 

Yes.
So the result ((resp->nlmsg_type == NLMSG_ERROR) && (err->error == 0)) should
be regarded as a success.

I feel like that this piece of code can be like:

    if (((resp->nlmsg_type != NLMSG_ERROR) || (*error != 0)) &&
        (resp->nlmsg_type != NLMSG_DONE) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                _("malformed netlink response message"));
        return -1;
    }

    return 0;

And the other three functions (DumpLink||DelLink|GetNeighbor) have the same
problem, I will fix them also.

>
>.. return 0. With your patch, I get the "malrofmed netlink response
>message" error. Reading netlink(7) manpage lets more light into this:
>
>   For reliable transfer the sender can request an acknowledgement from
>   the receiver by setting the NLM_F_ACK flag. An acknowledgment is an
>   NLMSG_ERROR packet with the error field set to 0.
>
>Which is exactly what I'm seeing, but what I don't understand is why we
>are getting this ack message since NLM_F_ACK flag was not set. 

It seems that the auto-ack is the default behaviour for netlink internal socket.
The netlink socke will be with NLM_F_ACK unless we call nl_socket_disable_auto_ack
explicitely.

Shi Lei

>
>Michal
>




More information about the libvir-list mailing list