]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/netlink.py
Fixed new file addition breakage with 0b762139
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / netlink.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Scott Feldman, sfeldma@cumulusnetworks.com
5 #
6
7 from os import strerror
8 import select
9 from time import time
10 import socket
11 from ctypes import *
12 from errno import *
13 import logging
14
15 logger = logging.getLogger(__name__)
16
17 #
18 # from /usr/include/linux/netlink.h
19 #
20
21 NETLINK_ROUTE = 0 # Routing/device hook
22 NETLINK_UNUSED = 1 # Unused number
23 NETLINK_USERSOCK = 2 # Reserved for user mode socket protocols
24 NETLINK_FIREWALL = 3 # Firewalling hook
25 NETLINK_INET_DIAG = 4 # INET socket monitoring
26 NETLINK_NFLOG = 5 # netfilter/iptables ULOG
27 NETLINK_XFRM = 6 # ipsec
28 NETLINK_SELINUX = 7 # SELinux event notifications
29 NETLINK_ISCSI = 8 # Open-iSCSI
30 NETLINK_AUDIT = 9 # auditing
31 NETLINK_FIB_LOOKUP = 10
32 NETLINK_CONNECTOR = 11
33 NETLINK_NETFILTER = 12 # netfilter subsystem
34 NETLINK_IP6_FW = 13
35 NETLINK_DNRTMSG = 14 # DECnet routing messages
36 NETLINK_KOBJECT_UEVENT = 15 # Kernel messages to userspace
37 NETLINK_GENERIC = 16
38 NETLINK_SCSITRANSPORT = 18 # SCSI Transports
39 NETLINK_ECRYPTFS = 19
40 NETLINK_RDMA = 20
41 NETLINK_CRYPTO = 21 # Crypto layer
42
43 NLMSG_NOOP = 1 # Nothing.
44 NLMSG_ERROR = 2 # Error
45 NLMSG_DONE = 3 # End of a dump
46 NLMSG_OVERRUN = 4 # Data lost
47
48 NETLINK_NO_ENOBUFS = 5
49
50 SOL_NETLINK = 270
51
52 class Nlmsghdr(Structure):
53
54 _fields_ = [
55 ('nlmsg_len', c_uint32),
56 ('nlmsg_type', c_uint16),
57 ('nlmsg_flags', c_uint16),
58 ('nlmsg_seq', c_uint32),
59 ('nlmsg_pid', c_uint32)
60 ]
61
62 def dump(self):
63 print 'nlmsg_len', self.nlmsg_len
64 print 'nlmsg_type', self.nlmsg_type
65 print 'nlmsg_flags 0x%04x' % self.nlmsg_flags
66 print 'nlmsg_seq', self.nlmsg_seq
67 print 'nlmsg_pid', self.nlmsg_pid
68
69 # Flags values
70
71 NLM_F_REQUEST = 1 # It is request message.
72 NLM_F_MULTI = 2 # Multipart message, terminated by NLMSG_DONE
73 NLM_F_ACK = 4 # Reply with ack, with zero or error code
74 NLM_F_ECHO = 8 # Echo this request
75 NLM_F_DUMP_INTR = 16 # Dump was inconsistent due to sequence change
76
77 # Modifiers to GET request
78 NLM_F_ROOT = 0x100 # specify tree root
79 NLM_F_MATCH = 0x200 # return all matching
80 NLM_F_ATOMIC = 0x400 # atomic GET
81 NLM_F_DUMP = (NLM_F_ROOT|NLM_F_MATCH)
82
83 # Modifiers to NEW request
84 NLM_F_REPLACE = 0x100 # Override existing
85 NLM_F_EXCL = 0x200 # Do not touch, if it exists
86 NLM_F_CREATE = 0x400 # Create, if it does not exist
87 NLM_F_APPEND = 0x800 # Add to end of list
88
89 NLMSG_ALIGNTO = 4
90 def NLMSG_ALIGN(len):
91 return (len + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)
92 def NLMSG_HDRLEN():
93 return NLMSG_ALIGN(sizeof(Nlmsghdr))
94 def NLMSG_LENGTH(len):
95 return len + NLMSG_ALIGN(NLMSG_HDRLEN())
96 def NLMSG_SPACE(len):
97 return NLMSG_ALIGN(NLMSG_LENGTH(len))
98 def NLMSG_DATA(nlh):
99 return addressof(nlh) + NLMSG_LENGTH(0)
100 def NLMSG_NEXT(nlh, len):
101 cur = NLMSG_ALIGN(nlh.nlmsg_len)
102 nlh = Nlmsghdr.from_address(addressof(nlh) + cur)
103 return len - cur, nlh
104 def NLMSG_OK(nlh, len):
105 return len >= sizeof(Nlmsghdr) and \
106 nlh.nlmsg_len >= sizeof(Nlmsghdr) and \
107 nlh.nlmsg_len <= len
108
109 class Nlmsgerr(Structure):
110
111 _fields_ = [
112 ('error', c_int),
113 ('msg', Nlmsghdr),
114 ]
115
116 class NetlinkError(Exception):
117
118 def __init__(self, message):
119 Exception.__init__(self, message)
120 #print(message)
121
122 class Netlink(socket.socket):
123
124 def __init__(self, pid, proto):
125
126 self.pid = pid
127 self.recvbuf = bytearray(8 * 1024)
128 self.sendbuf = bytearray(8 * 1024)
129 self.seq = int(time())
130
131 try:
132
133 socket.socket.__init__(self, socket.AF_NETLINK, \
134 socket.SOCK_RAW, proto)
135 self.setblocking(0)
136
137 # Need to turn off ENOBUFS for netlink socket otherwise
138 # in a kernel overrun situation, the socket will return
139 # ENOBUFS on socket recv and be stuck for future recvs.
140
141 self.setsockopt(SOL_NETLINK, NETLINK_NO_ENOBUFS, 1)
142
143 except socket.error as (errno, string):
144 raise NetlinkError("open: socket err[%d]: %s" % \
145 (errno, string))
146
147 def bind(self, groups, cb):
148
149 self._nl_cb = cb
150
151 try:
152 socket.socket.bind(self, (self.pid, groups))
153
154 except socket.error as (errno, string):
155 raise NetlinkError("bind: socket err[%d]: %s" % \
156 (errno, string))
157
158 def sendall(self, string):
159 try:
160 socket.socket.sendall(self, string)
161 except socket.error as (errno, string):
162 raise NetlinkError("send: socket err[%d]: %s" % \
163 (errno, string))
164
165 def _process_nlh(self, recv, nlh):
166 while NLMSG_OK(nlh, recv):
167 yield recv, nlh
168 recv, nlh = NLMSG_NEXT(nlh, recv)
169
170 def process(self, tokens=[]):
171
172 found_done = False
173
174 try:
175 recv, src_addr = self.recvfrom_into(self.recvbuf)
176 if not recv:
177 # EOF
178 print "EOF"
179 return False
180
181 except socket.error as (errno, string):
182 if errno in [EINTR, EAGAIN]:
183 return False
184 raise NetlinkError("netlink: socket err[%d]: %s" % \
185 (errno, string))
186
187 nlh = Nlmsghdr.from_buffer(self.recvbuf)
188 for recv, nlh in self._process_nlh(recv, nlh):
189
190 # print "type %u, seq %u, pid %u" % \
191 # (nlh.nlmsg_type, nlh.nlmsg_seq, nlh.nlmsg_pid)
192
193 l = nlh.nlmsg_len - sizeof(Nlmsghdr)
194
195 if l < 0 or nlh.nlmsg_len > recv:
196 raise NetlinkError("netlink: malformed msg: len %d" % \
197 nlh.nlmsg_len)
198
199 if tokens:
200 current = (nlh.nlmsg_pid, nlh.nlmsg_seq)
201 if current not in tokens:
202 continue
203
204 if nlh.nlmsg_type == NLMSG_DONE:
205 found_done = True
206 break
207
208 if nlh.nlmsg_type == NLMSG_ERROR:
209 err = Nlmsgerr.from_address(NLMSG_DATA(nlh))
210 if err.error == 0:
211 return False
212 raise NetlinkError("netlink: %s" % strerror(abs(err.error)))
213
214 if self._nl_cb:
215 self._nl_cb(nlh)
216
217 if found_done:
218 return False
219
220 remnant = recv - NLMSG_ALIGN(nlh.nlmsg_len) > 0
221 if remnant:
222 raise NetlinkError("netlink: remnant of size %d" % \
223 remnant)
224
225 return True
226
227 def process_wait(self, tokens):
228 while self.process(tokens):
229 pass
230
231 def process_forever(self):
232 epoll = select.epoll()
233 epoll.register(self.fileno(), select.EPOLLIN)
234 while True:
235 events = epoll.poll()
236 for fileno, event in events:
237 if fileno == self.fileno():
238 self.process()
239
240 def process_event(self, event):
241 return self.process()