9 #include <netinet/in.h>
12 #define IFADDRS_HASH_SIZE 64
14 /* getifaddrs() reports hardware addresses with PF_PACKET that implies
15 * struct sockaddr_ll. But e.g. Infiniband socket address length is
16 * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
17 * to extend ssl_addr - callers should be able to still use it. */
18 struct sockaddr_ll_hack
{
19 unsigned short sll_family
, sll_protocol
;
21 unsigned short sll_hatype
;
22 unsigned char sll_pkttype
, sll_halen
;
23 unsigned char sll_addr
[24];
28 struct sockaddr_ll_hack ll
;
29 struct sockaddr_in v4
;
30 struct sockaddr_in6 v6
;
33 struct ifaddrs_storage
{
35 struct ifaddrs_storage
*hash_next
;
36 union sockany addr
, netmask
, ifu
;
38 char name
[IFNAMSIZ
+1];
42 struct ifaddrs
*first
;
44 struct ifaddrs_storage
*hash
[IFADDRS_HASH_SIZE
];
47 void freeifaddrs(struct ifaddrs
*ifp
)
57 static void copy_addr(struct sockaddr
**r
, int af
, union sockany
*sa
, void *addr
, size_t addrlen
, int ifindex
)
64 dst
= (uint8_t*) &sa
->v4
.sin_addr
;
68 dst
= (uint8_t*) &sa
->v6
.sin6_addr
;
70 if (IN6_IS_ADDR_LINKLOCAL(addr
) || IN6_IS_ADDR_MC_LINKLOCAL(addr
))
71 sa
->v6
.sin6_scope_id
= ifindex
;
76 if (addrlen
< len
) return;
77 sa
->sa
.sa_family
= af
;
78 memcpy(dst
, addr
, len
);
82 static void gen_netmask(struct sockaddr
**r
, int af
, union sockany
*sa
, int prefixlen
)
84 uint8_t addr
[16] = {0};
87 if (prefixlen
> 8*sizeof(addr
)) prefixlen
= 8*sizeof(addr
);
89 memset(addr
, 0xff, i
);
90 if (i
< sizeof(addr
)) addr
[i
++] = 0xff << (8 - (prefixlen
% 8));
91 copy_addr(r
, af
, sa
, addr
, sizeof(addr
), 0);
94 static void copy_lladdr(struct sockaddr
**r
, union sockany
*sa
, void *addr
, size_t addrlen
, int ifindex
, unsigned short hatype
)
96 if (addrlen
> sizeof(sa
->ll
.sll_addr
)) return;
97 sa
->ll
.sll_family
= AF_PACKET
;
98 sa
->ll
.sll_ifindex
= ifindex
;
99 sa
->ll
.sll_hatype
= hatype
;
100 sa
->ll
.sll_halen
= addrlen
;
101 memcpy(sa
->ll
.sll_addr
, addr
, addrlen
);
105 static int netlink_msg_to_ifaddr(void *pctx
, struct nlmsghdr
*h
)
107 struct ifaddrs_ctx
*ctx
= pctx
;
108 struct ifaddrs_storage
*ifs
, *ifs0
;
109 struct ifinfomsg
*ifi
= NLMSG_DATA(h
);
110 struct ifaddrmsg
*ifa
= NLMSG_DATA(h
);
114 if (h
->nlmsg_type
== RTM_NEWLINK
) {
115 for (rta
= NLMSG_RTA(h
, sizeof(*ifi
)); NLMSG_RTAOK(rta
, h
); rta
= RTA_NEXT(rta
)) {
116 if (rta
->rta_type
!= IFLA_STATS
) continue;
117 stats_len
= RTA_DATALEN(rta
);
121 for (ifs0
= ctx
->hash
[ifa
->ifa_index
% IFADDRS_HASH_SIZE
]; ifs0
; ifs0
= ifs0
->hash_next
)
122 if (ifs0
->index
== ifa
->ifa_index
)
127 ifs
= calloc(1, sizeof(struct ifaddrs_storage
) + stats_len
);
128 if (ifs
== 0) return -1;
130 if (h
->nlmsg_type
== RTM_NEWLINK
) {
131 ifs
->index
= ifi
->ifi_index
;
132 ifs
->ifa
.ifa_flags
= ifi
->ifi_flags
;
134 for (rta
= NLMSG_RTA(h
, sizeof(*ifi
)); NLMSG_RTAOK(rta
, h
); rta
= RTA_NEXT(rta
)) {
135 switch (rta
->rta_type
) {
137 if (RTA_DATALEN(rta
) < sizeof(ifs
->name
)) {
138 memcpy(ifs
->name
, RTA_DATA(rta
), RTA_DATALEN(rta
));
139 ifs
->ifa
.ifa_name
= ifs
->name
;
143 copy_lladdr(&ifs
->ifa
.ifa_addr
, &ifs
->addr
, RTA_DATA(rta
), RTA_DATALEN(rta
), ifi
->ifi_index
, ifi
->ifi_type
);
146 copy_lladdr(&ifs
->ifa
.ifa_broadaddr
, &ifs
->ifu
, RTA_DATA(rta
), RTA_DATALEN(rta
), ifi
->ifi_index
, ifi
->ifi_type
);
149 ifs
->ifa
.ifa_data
= (void*)(ifs
+1);
150 memcpy(ifs
->ifa
.ifa_data
, RTA_DATA(rta
), RTA_DATALEN(rta
));
154 if (ifs
->ifa
.ifa_name
) {
155 unsigned int bucket
= ifs
->index
% IFADDRS_HASH_SIZE
;
156 ifs
->hash_next
= ctx
->hash
[bucket
];
157 ctx
->hash
[bucket
] = ifs
;
160 ifs
->ifa
.ifa_name
= ifs0
->ifa
.ifa_name
;
161 ifs
->ifa
.ifa_flags
= ifs0
->ifa
.ifa_flags
;
162 for (rta
= NLMSG_RTA(h
, sizeof(*ifa
)); NLMSG_RTAOK(rta
, h
); rta
= RTA_NEXT(rta
)) {
163 switch (rta
->rta_type
) {
165 /* If ifa_addr is already set we, received an IFA_LOCAL before
166 * so treat this as destination address */
167 if (ifs
->ifa
.ifa_addr
)
168 copy_addr(&ifs
->ifa
.ifa_dstaddr
, ifa
->ifa_family
, &ifs
->ifu
, RTA_DATA(rta
), RTA_DATALEN(rta
), ifa
->ifa_index
);
170 copy_addr(&ifs
->ifa
.ifa_addr
, ifa
->ifa_family
, &ifs
->addr
, RTA_DATA(rta
), RTA_DATALEN(rta
), ifa
->ifa_index
);
173 copy_addr(&ifs
->ifa
.ifa_broadaddr
, ifa
->ifa_family
, &ifs
->ifu
, RTA_DATA(rta
), RTA_DATALEN(rta
), ifa
->ifa_index
);
176 /* If ifa_addr is set and we get IFA_LOCAL, assume we have
177 * a point-to-point network. Move address to correct field. */
178 if (ifs
->ifa
.ifa_addr
) {
179 ifs
->ifu
= ifs
->addr
;
180 ifs
->ifa
.ifa_dstaddr
= &ifs
->ifu
.sa
;
181 memset(&ifs
->addr
, 0, sizeof(ifs
->addr
));
183 copy_addr(&ifs
->ifa
.ifa_addr
, ifa
->ifa_family
, &ifs
->addr
, RTA_DATA(rta
), RTA_DATALEN(rta
), ifa
->ifa_index
);
186 if (RTA_DATALEN(rta
) < sizeof(ifs
->name
)) {
187 memcpy(ifs
->name
, RTA_DATA(rta
), RTA_DATALEN(rta
));
188 ifs
->ifa
.ifa_name
= ifs
->name
;
193 if (ifs
->ifa
.ifa_addr
)
194 gen_netmask(&ifs
->ifa
.ifa_netmask
, ifa
->ifa_family
, &ifs
->netmask
, ifa
->ifa_prefixlen
);
197 if (ifs
->ifa
.ifa_name
) {
198 if (!ctx
->first
) ctx
->first
= &ifs
->ifa
;
199 if (ctx
->last
) ctx
->last
->ifa_next
= &ifs
->ifa
;
200 ctx
->last
= &ifs
->ifa
;
207 int getifaddrs(struct ifaddrs
**ifap
)
209 struct ifaddrs_ctx _ctx
, *ctx
= &_ctx
;
211 memset(ctx
, 0, sizeof *ctx
);
212 r
= __rtnetlink_enumerate(AF_UNSPEC
, AF_UNSPEC
, netlink_msg_to_ifaddr
, ctx
);
213 if (r
== 0) *ifap
= ctx
->first
;
214 else freeifaddrs(ctx
->first
);