C++程序  |  148行  |  3.88 KB

/* external/dhcpcd/ifaddrs.c
**
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");.
** you may not use this file except in compliance with the License..
** You may obtain a copy of the License at.
**
**     http://www.apache.org/licenses/LICENSE-2.0.
**
** Unless required by applicable law or agreed to in writing, software.
** distributed under the License is distributed on an "AS IS" BASIS,.
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied..
** See the License for the specific language governing permissions and.
** limitations under the License.
*/

#include <arpa/inet.h>
#include <sys/socket.h>
#include "ifaddrs.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <netinet/ether.h>
#include <netdb.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>
#include <linux/if_arp.h>
#include <netutils/ifc.h>

struct ifaddrs *get_interface(const char *name, sa_family_t family)
{
    unsigned addr, flags;
    int masklen;
    struct ifaddrs *ifa;
    struct sockaddr_in *saddr = NULL;
    struct sockaddr_in *smask = NULL;
    struct sockaddr_ll *hwaddr = NULL;
    unsigned char hwbuf[ETH_ALEN];

    if (ifc_get_info(name, &addr, &masklen, &flags))
        return NULL;

    if ((family == AF_INET) && (addr == 0))
        return NULL;

    ifa = malloc(sizeof(struct ifaddrs));
    if (!ifa)
        return NULL;
    memset(ifa, 0, sizeof(struct ifaddrs));

    ifa->ifa_name = malloc(strlen(name)+1);
    if (!ifa->ifa_name) {
        free(ifa);
        return NULL;
    }
    strcpy(ifa->ifa_name, name);
    ifa->ifa_flags = flags;

    if (family == AF_INET) {
        saddr = malloc(sizeof(struct sockaddr_in));
        if (saddr) {
            saddr->sin_addr.s_addr = addr;
            saddr->sin_family = family;
        }
        ifa->ifa_addr = (struct sockaddr *)saddr;

        if (masklen != 0) {
            smask = malloc(sizeof(struct sockaddr_in));
            if (smask) {
                smask->sin_addr.s_addr = prefixLengthToIpv4Netmask(masklen);
                smask->sin_family = family;
            }
        }
        ifa->ifa_netmask = (struct sockaddr *)smask;
    } else if (family == AF_PACKET) {
        if (!ifc_get_hwaddr(name, hwbuf)) {
            hwaddr = malloc(sizeof(struct sockaddr_ll));
            if (hwaddr) {
                memset(hwaddr, 0, sizeof(struct sockaddr_ll));
                hwaddr->sll_family = family;
                /* hwaddr->sll_protocol = ETHERTYPE_IP; */
                hwaddr->sll_hatype = ARPHRD_ETHER;
                hwaddr->sll_halen = ETH_ALEN;
                memcpy(hwaddr->sll_addr, hwbuf, ETH_ALEN);
            }
        }
        ifa->ifa_addr = (struct sockaddr *)hwaddr;
        ifa->ifa_netmask = (struct sockaddr *)smask;
    }
    return ifa;
}

int getifaddrs(struct ifaddrs **ifap)
{
    DIR *d;
    struct dirent *de;
    struct ifaddrs *ifa;
    struct ifaddrs *ifah = NULL;

    if (!ifap)
        return -1;
    *ifap = NULL;

    if (ifc_init())
       return -1;

    d = opendir("/sys/class/net");
    if (d == 0)
        return -1;
    while ((de = readdir(d))) {
        if (de->d_name[0] == '.')
            continue;
        ifa = get_interface(de->d_name, AF_INET);
        if (ifa != NULL) {
            ifa->ifa_next = ifah;
            ifah = ifa;
        }
        ifa = get_interface(de->d_name, AF_PACKET);
        if (ifa != NULL) {
            ifa->ifa_next = ifah;
            ifah = ifa;
        }
    }
    *ifap = ifah;
    closedir(d);
    ifc_close();
    return 0;
}

void freeifaddrs(struct ifaddrs *ifa)
{
    struct ifaddrs *ifp;

    while (ifa) {
        ifp = ifa;
        free(ifp->ifa_name);
        if (ifp->ifa_addr)
            free(ifp->ifa_addr);
        if (ifp->ifa_netmask)
            free(ifp->ifa_netmask);
        ifa = ifa->ifa_next;
        free(ifp);
    }
}