]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdown/netlink.py
add new ifupdown2.conf option ifreload_down_changed to control ifreload
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / netlink.py
CommitLineData
f82758bf
RP
1#!/usr/bin/env python
2#
3# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4# Author: Scott Feldman, sfeldma@cumulusnetworks.com
5#
6
7from os import strerror
8import select
9from time import time
10import socket
11from ctypes import *
12from errno import *
13import logging
14
15logger = logging.getLogger(__name__)
16
17#
18# from /usr/include/linux/netlink.h
19#
20
21NETLINK_ROUTE = 0 # Routing/device hook
22NETLINK_UNUSED = 1 # Unused number
23NETLINK_USERSOCK = 2 # Reserved for user mode socket protocols
24NETLINK_FIREWALL = 3 # Firewalling hook
25NETLINK_INET_DIAG = 4 # INET socket monitoring
26NETLINK_NFLOG = 5 # netfilter/iptables ULOG
27NETLINK_XFRM = 6 # ipsec
28NETLINK_SELINUX = 7 # SELinux event notifications
29NETLINK_ISCSI = 8 # Open-iSCSI
30NETLINK_AUDIT = 9 # auditing
31NETLINK_FIB_LOOKUP = 10
32NETLINK_CONNECTOR = 11
33NETLINK_NETFILTER = 12 # netfilter subsystem
34NETLINK_IP6_FW = 13
35NETLINK_DNRTMSG = 14 # DECnet routing messages
36NETLINK_KOBJECT_UEVENT = 15 # Kernel messages to userspace
37NETLINK_GENERIC = 16
38NETLINK_SCSITRANSPORT = 18 # SCSI Transports
39NETLINK_ECRYPTFS = 19
40NETLINK_RDMA = 20
41NETLINK_CRYPTO = 21 # Crypto layer
42
43NLMSG_NOOP = 1 # Nothing.
44NLMSG_ERROR = 2 # Error
45NLMSG_DONE = 3 # End of a dump
46NLMSG_OVERRUN = 4 # Data lost
47
48NETLINK_NO_ENOBUFS = 5
49
50SOL_NETLINK = 270
51
52class 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
71NLM_F_REQUEST = 1 # It is request message.
72NLM_F_MULTI = 2 # Multipart message, terminated by NLMSG_DONE
73NLM_F_ACK = 4 # Reply with ack, with zero or error code
74NLM_F_ECHO = 8 # Echo this request
75NLM_F_DUMP_INTR = 16 # Dump was inconsistent due to sequence change
76
77# Modifiers to GET request
78NLM_F_ROOT = 0x100 # specify tree root
79NLM_F_MATCH = 0x200 # return all matching
80NLM_F_ATOMIC = 0x400 # atomic GET
81NLM_F_DUMP = (NLM_F_ROOT|NLM_F_MATCH)
82
83# Modifiers to NEW request
84NLM_F_REPLACE = 0x100 # Override existing
85NLM_F_EXCL = 0x200 # Do not touch, if it exists
86NLM_F_CREATE = 0x400 # Create, if it does not exist
87NLM_F_APPEND = 0x800 # Add to end of list
88
89NLMSG_ALIGNTO = 4
90def NLMSG_ALIGN(len):
91 return (len + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)
92def NLMSG_HDRLEN():
93 return NLMSG_ALIGN(sizeof(Nlmsghdr))
94def NLMSG_LENGTH(len):
95 return len + NLMSG_ALIGN(NLMSG_HDRLEN())
96def NLMSG_SPACE(len):
97 return NLMSG_ALIGN(NLMSG_LENGTH(len))
98def NLMSG_DATA(nlh):
99 return addressof(nlh) + NLMSG_LENGTH(0)
100def NLMSG_NEXT(nlh, len):
101 cur = NLMSG_ALIGN(nlh.nlmsg_len)
102 nlh = Nlmsghdr.from_address(addressof(nlh) + cur)
103 return len - cur, nlh
104def NLMSG_OK(nlh, len):
105 return len >= sizeof(Nlmsghdr) and \
106 nlh.nlmsg_len >= sizeof(Nlmsghdr) and \
107 nlh.nlmsg_len <= len
108
109class Nlmsgerr(Structure):
110
111 _fields_ = [
112 ('error', c_int),
113 ('msg', Nlmsghdr),
114 ]
115
116class NetlinkError(Exception):
117
118 def __init__(self, message):
119 Exception.__init__(self, message)
120 #print(message)
121
122class 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()