1 /*
2  * virmacaddr.c: MAC address handling
3  *
4  * Copyright (C) 2006-2013 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 
24 #include "virmacaddr.h"
25 #include "virrandom.h"
26 #include "viralloc.h"
27 
28 static const unsigned char virMacAddrBroadcastAddrRaw[VIR_MAC_BUFLEN] =
29     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
30 
31 /* Compare two MAC addresses, ignoring differences in case,
32  * as well as leading zeros.
33  */
34 int
virMacAddrCompare(const char * p,const char * q)35 virMacAddrCompare(const char *p, const char *q)
36 {
37     unsigned char c, d;
38     do {
39         while (*p == '0' && g_ascii_isxdigit(p[1]))
40             ++p;
41         while (*q == '0' && g_ascii_isxdigit(q[1]))
42             ++q;
43         c = g_ascii_tolower(*p);
44         d = g_ascii_tolower(*q);
45 
46         if (c == 0 || d == 0)
47             break;
48 
49         ++p;
50         ++q;
51     } while (c == d);
52 
53     if (UCHAR_MAX <= INT_MAX)
54         return c - d;
55 
56     /* On machines where 'char' and 'int' are types of the same size, the
57        difference of two 'unsigned char' values - including the sign bit -
58        doesn't fit in an 'int'.  */
59     return c > d ? 1 : c < d ? -1 : 0;
60 }
61 
62 /**
63  * virMacAddrCmp:
64  * @mac1: pointer to 1st MAC address
65  * @mac2: pointer to 2nd MAC address
66  *
67  * Return 0 if MAC addresses are equal,
68  * < 0 if mac1 < mac2,
69  * > 0 if mac1 > mac2
70  */
71 int
virMacAddrCmp(const virMacAddr * mac1,const virMacAddr * mac2)72 virMacAddrCmp(const virMacAddr *mac1, const virMacAddr *mac2)
73 {
74     return memcmp(mac1->addr, mac2->addr, VIR_MAC_BUFLEN);
75 }
76 
77 /**
78  * virMacAddrCmpRaw:
79  * @mac1: pointer to 1st MAC address
80  * @mac2: pointer to 2nd MAC address in plain buffer
81  *
82  * Return 0 if MAC addresses are equal,
83  * < 0 if mac1 < mac2,
84  * > 0 if mac1 > mac2
85  */
86 int
virMacAddrCmpRaw(const virMacAddr * mac1,const unsigned char mac2[VIR_MAC_BUFLEN])87 virMacAddrCmpRaw(const virMacAddr *mac1,
88                  const unsigned char mac2[VIR_MAC_BUFLEN])
89 {
90     return memcmp(mac1->addr, mac2, VIR_MAC_BUFLEN);
91 }
92 
93 /**
94  * virMacAddrSet
95  * @dst: pointer to destination
96  * @src: pointer to source
97  *
98  * Copy src to dst
99  */
100 void
virMacAddrSet(virMacAddr * dst,const virMacAddr * src)101 virMacAddrSet(virMacAddr *dst, const virMacAddr *src)
102 {
103     memcpy(dst, src, sizeof(*src));
104 }
105 
106 /**
107  * virMacAddrSetRaw
108  * @dst: pointer to destination to hold MAC address
109  * @src: raw MAC address data
110  *
111  * Set the MAC address to the given value
112  */
113 void
virMacAddrSetRaw(virMacAddr * dst,const unsigned char src[VIR_MAC_BUFLEN])114 virMacAddrSetRaw(virMacAddr *dst, const unsigned char src[VIR_MAC_BUFLEN])
115 {
116     memcpy(dst->addr, src, VIR_MAC_BUFLEN);
117 }
118 
119 /**
120  * virMacAddrGetRaw
121  * @src: pointer to MAC address
122  * @dst: pointer to raw memory to write MAC address into
123  *
124  * Copies the MAC address into raw memory
125  */
126 void
virMacAddrGetRaw(const virMacAddr * src,unsigned char dst[VIR_MAC_BUFLEN])127 virMacAddrGetRaw(const virMacAddr *src, unsigned char dst[VIR_MAC_BUFLEN])
128 {
129     memcpy(dst, src->addr, VIR_MAC_BUFLEN);
130 }
131 
132 /**
133  * virMacAddrParse:
134  * @str: string representation of MAC address, e.g., "0:1E:FC:E:3a:CB"
135  * @addr: 6-byte MAC address
136  *
137  * Parse a MAC address
138  *
139  * Return 0 upon success, or -1 in case of error.
140  */
141 int
virMacAddrParse(const char * str,virMacAddr * addr)142 virMacAddrParse(const char* str, virMacAddr *addr)
143 {
144     size_t i;
145 
146     errno = 0;
147     for (i = 0; i < VIR_MAC_BUFLEN; i++) {
148         char *end_ptr;
149         unsigned long result;
150 
151         /* This is solely to avoid accepting the leading
152          * space or "+" that strtoul would otherwise accept.
153          */
154         if (!g_ascii_isxdigit(*str))
155             break;
156 
157         result = strtoul(str, &end_ptr, 16); /* exempt from syntax-check */
158 
159         if ((end_ptr - str) < 1 || 2 < (end_ptr - str) ||
160             (errno != 0) ||
161             (0xFF < result))
162             break;
163 
164         addr->addr[i] = (unsigned char) result;
165 
166         if ((i == 5) && (*end_ptr <= ' '))
167             return 0;
168         if (*end_ptr != ':')
169             break;
170 
171         str = end_ptr + 1;
172     }
173 
174     return -1;
175 }
176 
177 /* virMacAddrFormat
178  * Converts the binary mac address in addr into a NULL-terminated
179  * character string in str. It is assumed that the memory pointed to
180  * by str is at least VIR_MAC_STRING_BUFLEN bytes long.
181  *
182  * Returns a pointer to the resulting character string.
183  */
184 const char *
virMacAddrFormat(const virMacAddr * addr,char * str)185 virMacAddrFormat(const virMacAddr *addr,
186                  char *str)
187 {
188     g_snprintf(str, VIR_MAC_STRING_BUFLEN,
189                "%02x:%02x:%02x:%02x:%02x:%02x",
190                addr->addr[0], addr->addr[1], addr->addr[2],
191                addr->addr[3], addr->addr[4], addr->addr[5]);
192     str[VIR_MAC_STRING_BUFLEN-1] = '\0';
193     return str;
194 }
195 
196 /**
197  * virMacAddrParseHex:
198  * @str: string hexadecimal representation of MAC address, e.g., "F801EFCE3aCB"
199  * @addr: 6-byte MAC address
200  *
201  * Parse the hexadecimal representation of a MAC address
202  *
203  * Return 0 upon success, or -1 in case of error.
204  */
205 int
virMacAddrParseHex(const char * str,virMacAddr * addr)206 virMacAddrParseHex(const char *str, virMacAddr *addr)
207 {
208     size_t i;
209 
210     if (strspn(str, "0123456789abcdefABCDEF") != VIR_MAC_HEXLEN ||
211         str[VIR_MAC_HEXLEN])
212         return -1;
213 
214     for (i = 0; i < VIR_MAC_BUFLEN; i++)
215         addr->addr[i] = (g_ascii_xdigit_value(str[2 * i]) << 4 |
216                          g_ascii_xdigit_value(str[2 * i + 1]));
217     return 0;
218 }
219 
virMacAddrGenerate(const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN],virMacAddr * addr)220 void virMacAddrGenerate(const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN],
221                         virMacAddr *addr)
222 {
223     addr->addr[0] = prefix[0];
224     addr->addr[1] = prefix[1];
225     addr->addr[2] = prefix[2];
226     addr->addr[3] = virRandomBits(8);
227     addr->addr[4] = virRandomBits(8);
228     addr->addr[5] = virRandomBits(8);
229 }
230 
231 /* The low order bit of the first byte is the "multicast" bit. */
232 bool
virMacAddrIsMulticast(const virMacAddr * mac)233 virMacAddrIsMulticast(const virMacAddr *mac)
234 {
235     return !!(mac->addr[0] & 1);
236 }
237 
238 bool
virMacAddrIsUnicast(const virMacAddr * mac)239 virMacAddrIsUnicast(const virMacAddr *mac)
240 {
241     return !(mac->addr[0] & 1);
242 }
243 
244 bool
virMacAddrIsBroadcastRaw(const unsigned char s[VIR_MAC_BUFLEN])245 virMacAddrIsBroadcastRaw(const unsigned char s[VIR_MAC_BUFLEN])
246 {
247     return memcmp(virMacAddrBroadcastAddrRaw, s, sizeof(*s)) == 0;
248 }
249 
250 void
virMacAddrFree(virMacAddr * addr)251 virMacAddrFree(virMacAddr *addr)
252 {
253     g_free(addr);
254 }
255