Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/netaddr/strategy/eui48.py: 45%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

128 statements  

1# ----------------------------------------------------------------------------- 

2# Copyright (c) 2008 by David P. D. Moss. All rights reserved. 

3# 

4# Released under the BSD license. See the LICENSE file for details. 

5# ----------------------------------------------------------------------------- 

6""" 

7IEEE 48-bit EUI (MAC address) logic. 

8 

9Supports numerous MAC string formats including Cisco's triple hextet as well 

10as bare MACs containing no delimiters. 

11""" 

12import struct as _struct 

13import re as _re 

14 

15 

16from netaddr.core import AddrFormatError 

17from netaddr.strategy import ( 

18 valid_words as _valid_words, 

19 int_to_words as _int_to_words, 

20 words_to_int as _words_to_int, 

21 valid_bits as _valid_bits, 

22 bits_to_int as _bits_to_int, 

23 int_to_bits as _int_to_bits, 

24 valid_bin as _valid_bin, 

25 int_to_bin as _int_to_bin, 

26 bin_to_int as _bin_to_int, 

27) 

28 

29#: The width (in bits) of this address type. 

30width = 48 

31 

32#: The version of this address type. 

33version = 48 

34 

35#: The maximum integer value that can be represented by this address type. 

36max_int = 2**width - 1 

37 

38# ----------------------------------------------------------------------------- 

39# Dialect classes. 

40# ----------------------------------------------------------------------------- 

41 

42 

43class mac_eui48(object): 

44 """A standard IEEE EUI-48 dialect class.""" 

45 

46 #: The individual word size (in bits) of this address type. 

47 word_size = 8 

48 

49 #: The number of words in this address type. 

50 num_words = width // word_size 

51 

52 #: The maximum integer value for an individual word in this address type. 

53 max_word = 2**word_size - 1 

54 

55 #: The separator character used between each word. 

56 word_sep = '-' 

57 

58 #: The format string to be used when converting words to string values. 

59 word_fmt = '%.2X' 

60 

61 #: The number base to be used when interpreting word values as integers. 

62 word_base = 16 

63 

64 

65class mac_unix(mac_eui48): 

66 """A UNIX-style MAC address dialect class.""" 

67 

68 word_size = 8 

69 num_words = width // word_size 

70 word_sep = ':' 

71 word_fmt = '%x' 

72 word_base = 16 

73 

74 

75class mac_unix_expanded(mac_unix): 

76 """A UNIX-style MAC address dialect class with leading zeroes.""" 

77 

78 word_fmt = '%.2x' 

79 

80 

81class mac_cisco(mac_eui48): 

82 """A Cisco 'triple hextet' MAC address dialect class.""" 

83 

84 word_size = 16 

85 num_words = width // word_size 

86 word_sep = '.' 

87 word_fmt = '%.4x' 

88 word_base = 16 

89 

90 

91class mac_bare(mac_eui48): 

92 """A bare (no delimiters) MAC address dialect class.""" 

93 

94 word_size = 48 

95 num_words = width // word_size 

96 word_sep = '' 

97 word_fmt = '%.12X' 

98 word_base = 16 

99 

100 

101class mac_pgsql(mac_eui48): 

102 """A PostgreSQL style (2 x 24-bit words) MAC address dialect class.""" 

103 

104 word_size = 24 

105 num_words = width // word_size 

106 word_sep = ':' 

107 word_fmt = '%.6x' 

108 word_base = 16 

109 

110 

111#: The default dialect to be used when not specified by the user. 

112DEFAULT_DIALECT = mac_eui48 

113 

114# ----------------------------------------------------------------------------- 

115#: Regular expressions to match all supported MAC address formats. 

116#: For efficiency, each string regexp converted in place to its compiled 

117#: counterpart. 

118RE_MAC_FORMATS = [ 

119 _re.compile(_, _re.IGNORECASE) 

120 for _ in ( 

121 # 2 bytes x 6 (UNIX, Windows, EUI-48) 

122 '^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$', 

123 '^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$', 

124 # 4 bytes x 3 (Cisco) 

125 '^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$', 

126 '^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$', 

127 '^' + r'\.'.join(['([0-9A-F]{1,4})'] * 3) + '$', 

128 # 6 bytes x 2 (PostgreSQL) 

129 '^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$', 

130 '^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$', 

131 # 12 bytes (bare, no delimiters) 

132 '^(' + ''.join(['[0-9A-F]'] * 12) + ')$', 

133 '^(' + ''.join(['[0-9A-F]'] * 11) + ')$', 

134 ) 

135] 

136 

137 

138def valid_str(addr): 

139 """ 

140 :param addr: An IEEE EUI-48 (MAC) address in string form. 

141 

142 :return: ``True`` if MAC address string is valid, ``False`` otherwise. 

143 """ 

144 for regexp in RE_MAC_FORMATS: 

145 try: 

146 match_result = regexp.findall(addr) 

147 if len(match_result) != 0: 

148 return True 

149 except TypeError: 

150 pass 

151 

152 return False 

153 

154 

155def str_to_int(addr): 

156 """ 

157 :param addr: An IEEE EUI-48 (MAC) address in string form. 

158 

159 :return: An unsigned integer that is equivalent to value represented 

160 by EUI-48/MAC string address formatted according to the dialect 

161 settings. 

162 """ 

163 words = [] 

164 if isinstance(addr, str): 

165 found_match = False 

166 for regexp in RE_MAC_FORMATS: 

167 match_result = regexp.findall(addr) 

168 if len(match_result) != 0: 

169 found_match = True 

170 if isinstance(match_result[0], tuple): 

171 words = match_result[0] 

172 else: 

173 words = (match_result[0],) 

174 break 

175 if not found_match: 

176 raise AddrFormatError('%r is not a supported MAC format!' % (addr,)) 

177 else: 

178 raise TypeError('%r is not str() or unicode()!' % (addr,)) 

179 

180 int_val = None 

181 

182 if len(words) == 6: 

183 # 2 bytes x 6 (UNIX, Windows, EUI-48) 

184 int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16) 

185 elif len(words) == 3: 

186 # 4 bytes x 3 (Cisco) 

187 int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16) 

188 elif len(words) == 2: 

189 # 6 bytes x 2 (PostgreSQL) 

190 int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16) 

191 elif len(words) == 1: 

192 # 12 bytes (bare, no delimiters) 

193 int_val = int('%012x' % int(words[0], 16), 16) 

194 else: 

195 raise AddrFormatError('unexpected word count in MAC address %r!' % (addr,)) 

196 

197 return int_val 

198 

199 

200def int_to_str(int_val, dialect=None): 

201 """ 

202 :param int_val: An unsigned integer. 

203 

204 :param dialect: (optional) a Python class defining formatting options. 

205 

206 :return: An IEEE EUI-48 (MAC) address string that is equivalent to 

207 unsigned integer formatted according to the dialect settings. 

208 """ 

209 if dialect is None: 

210 dialect = mac_eui48 

211 

212 words = int_to_words(int_val, dialect) 

213 tokens = [dialect.word_fmt % i for i in words] 

214 addr = dialect.word_sep.join(tokens) 

215 

216 return addr 

217 

218 

219def int_to_packed(int_val): 

220 """ 

221 :param int_val: the integer to be packed. 

222 

223 :return: a packed string that is equivalent to value represented by an 

224 unsigned integer. 

225 """ 

226 return _struct.pack('>HI', int_val >> 32, int_val & 0xFFFFFFFF) 

227 

228 

229def packed_to_int(packed_int): 

230 """ 

231 :param packed_int: a packed string containing an unsigned integer. 

232 It is assumed that string is packed in network byte order. 

233 

234 :return: An unsigned integer equivalent to value of network address 

235 represented by packed binary string. 

236 """ 

237 words = list(_struct.unpack('>6B', packed_int)) 

238 

239 int_val = 0 

240 for i, num in enumerate(reversed(words)): 

241 word = num 

242 word = word << 8 * i 

243 int_val = int_val | word 

244 

245 return int_val 

246 

247 

248def valid_words(words, dialect=None): 

249 if dialect is None: 

250 dialect = DEFAULT_DIALECT 

251 return _valid_words(words, dialect.word_size, dialect.num_words) 

252 

253 

254def int_to_words(int_val, dialect=None): 

255 if dialect is None: 

256 dialect = DEFAULT_DIALECT 

257 return _int_to_words(int_val, dialect.word_size, dialect.num_words) 

258 

259 

260def words_to_int(words, dialect=None): 

261 if dialect is None: 

262 dialect = DEFAULT_DIALECT 

263 return _words_to_int(words, dialect.word_size, dialect.num_words) 

264 

265 

266def valid_bits(bits, dialect=None): 

267 if dialect is None: 

268 dialect = DEFAULT_DIALECT 

269 return _valid_bits(bits, width, dialect.word_sep) 

270 

271 

272def bits_to_int(bits, dialect=None): 

273 if dialect is None: 

274 dialect = DEFAULT_DIALECT 

275 return _bits_to_int(bits, width, dialect.word_sep) 

276 

277 

278def int_to_bits(int_val, dialect=None): 

279 if dialect is None: 

280 dialect = DEFAULT_DIALECT 

281 return _int_to_bits(int_val, dialect.word_size, dialect.num_words, dialect.word_sep) 

282 

283 

284def valid_bin(bin_val, dialect=None): 

285 if dialect is None: 

286 dialect = DEFAULT_DIALECT 

287 return _valid_bin(bin_val, width) 

288 

289 

290def int_to_bin(int_val): 

291 return _int_to_bin(int_val, width) 

292 

293 

294def bin_to_int(bin_val): 

295 return _bin_to_int(bin_val, width)