File: monitor_linux.py

package info (click to toggle)
python-os-ken 4.1.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 21,396 kB
  • sloc: python: 100,059; erlang: 14,517; ansic: 594; sh: 338; makefile: 136
file content (218 lines) | stat: -rw-r--r-- 8,431 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import contextlib
import socket
import struct

from os_ken.controller import handler
from os_ken.ofproto import ether
from os_ken.ofproto import inet
from os_ken.lib import addrconv
from os_ken.lib import hub
from os_ken.lib.packet import arp
from os_ken.lib.packet import vrrp
from os_ken.services.protocols.vrrp import monitor
from os_ken.services.protocols.vrrp import event as vrrp_event
from os_ken.services.protocols.vrrp import utils


# Those are not defined in socket module
SS_MAXSIZE = 128
MCAST_JOIN_GROUP = 42
MCAST_LEAVE_GROUP = 45
PACKET_ADD_MEMBERSHIP = 1
PACKET_DROP_MEMBERSHIP = 2
PACKET_MR_MULTICAST = 0
SOL_PACKET = 263


def if_nametoindex(ifname):
    filename = '/sys/class/net/' + ifname + '/ifindex'
    with contextlib.closing(open(filename)) as f:
        for line in f:
            return int(line)


@monitor.VRRPInterfaceMonitor.register(vrrp_event.VRRPInterfaceNetworkDevice)
class VRRPInterfaceMonitorNetworkDevice(monitor.VRRPInterfaceMonitor):
    """
    This module uses raw socket so that privilege(CAP_NET_ADMIN capability)
    is required.
    """

    def __init__(self, *args, **kwargs):
        super(VRRPInterfaceMonitorNetworkDevice, self).__init__(*args,
                                                                **kwargs)
        self.__is_active = True
        config = self.config
        if config.is_ipv6:
            family = socket.AF_INET6
            ether_type = ether.ETH_TYPE_IPV6
            mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
        else:
            family = socket.AF_INET
            ether_type = ether.ETH_TYPE_IP
            mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
        # socket module doesn't define IPPROTO_VRRP
        self.ip_socket = socket.socket(family, socket.SOCK_RAW,
                                       inet.IPPROTO_VRRP)

        self.packet_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
                                           socket.htons(ether_type))
        self.packet_socket.bind((self.interface.device_name, ether_type,
                                 socket.PACKET_MULTICAST,
                                 arp.ARP_HW_TYPE_ETHERNET,
                                 addrconv.mac.text_to_bin(mac_address)))

        self.ifindex = if_nametoindex(self.interface.device_name)

    def start(self):
        # discard received packets before joining multicast membership
        packet_socket = self.packet_socket
        packet_socket.setblocking(0)
        with hub.Timeout(0.1, False):
            while True:
                try:
                    packet_socket.recv(1500)
                except socket.error:
                    break
        packet_socket.setblocking(1)

        self._join_multicast_membership(True)
        self._join_vrrp_group(True)
        super(VRRPInterfaceMonitorNetworkDevice, self).start()
        self.threads.append(hub.spawn(self._recv_loop))

    def stop(self):
        self.__is_active = False
        super(VRRPInterfaceMonitorNetworkDevice, self).stop()

    # we assume that the structures in the following two functions for
    # multicast are aligned in the same way on all the archtectures.
    def _join_multicast_membership(self, join_leave):
        config = self.config
        if config.is_ipv6:
            mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
        else:
            mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
        if join_leave:
            add_drop = PACKET_ADD_MEMBERSHIP
        else:
            add_drop = PACKET_DROP_MEMBERSHIP
        # struct packet_mreq {
        #     int mr_ifindex;
        #     unsigned short mr_type;
        #     unsigned short mr_alen;
        #     unsigned char  mr_mr_address[8];
        # };
        packet_mreq = struct.pack('IHH8s', self.ifindex,
                                  PACKET_MR_MULTICAST, 6,
                                  addrconv.mac.text_to_bin(mac_address))
        self.packet_socket.setsockopt(SOL_PACKET, add_drop, packet_mreq)

    def _join_vrrp_group(self, join_leave):
        if join_leave:
            join_leave = MCAST_JOIN_GROUP
        else:
            join_leave = MCAST_LEAVE_GROUP

        # struct group_req {
        #     __u32 gr_interface;  /* interface index */
        #     struct __kernel_sockaddr_storage gr_group; /* group address */
        # };
        group_req = struct.pack('I', self.ifindex)
        # padding to gr_group. This is environment dependent
        group_req += b'\x00' * (struct.calcsize('P') - struct.calcsize('I'))
        if self.config.is_ipv6:
            # struct sockaddr_in6 {
            #     sa_family_t     sin6_family;   /* AF_INET6 */
            #     in_port_t       sin6_port;     /* port number */
            #     uint32_t        sin6_flowinfo; /* IPv6 flow information */
            #     struct in6_addr sin6_addr;     /* IPv6 address */
            #     uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
            # };
            # struct in6_addr {
            #     unsigned char   s6_addr[16];   /* IPv6 address */
            # };
            family = socket.IPPROTO_IPV6
            sockaddr = struct.pack('H', socket.AF_INET6)
            sockaddr += struct.pack('!H', 0)
            sockaddr += struct.pack('!I', 0)
            sockaddr += addrconv.ipv6.text_to_bin(vrrp.VRRP_IPV6_DST_ADDRESS)
            sockaddr += struct.pack('I', 0)
        else:
            # #define __SOCK_SIZE__   16 /* sizeof(struct sockaddr) */
            # struct sockaddr_in {
            #   __kernel_sa_family_t  sin_family;     /* Address family */
            #   __be16                sin_port;       /* Port number */
            #   struct in_addr        sin_addr;       /* Internet address */
            #   /* Pad to size of `struct sockaddr'. */
            #   unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -
            #           sizeof(unsigned short int) - sizeof(struct in_addr)];
            # };
            # struct in_addr {
            #     __be32  s_addr;
            # };
            family = socket.IPPROTO_IP
            sockaddr = struct.pack('H', socket.AF_INET)
            sockaddr += struct.pack('!H', 0)
            sockaddr += addrconv.ipv4.text_to_bin(vrrp.VRRP_IPV4_DST_ADDRESS)

        sockaddr += b'\x00' * (SS_MAXSIZE - len(sockaddr))
        group_req += sockaddr

        self.ip_socket.setsockopt(family, join_leave, group_req)
        return

    def _recv_loop(self):
        packet_socket = self.packet_socket
        packet_socket.settimeout(1.3)       # to check activeness periodically
        try:
            while self.__is_active:
                try:
                    buf = packet_socket.recv(128)
                except socket.timeout:
                    self.logger.debug('timeout')
                    continue
                except:
                    self.logger.error('recv failed')
                    continue
                if len(buf) == 0:
                    self.__is_active = False
                    break

                self.logger.debug('recv buf')
                self._send_vrrp_packet_received(buf)
        finally:
            self._join_vrrp_group(False)
            self._join_multicast_membership(False)

    @handler.set_ev_handler(vrrp_event.EventVRRPTransmitRequest)
    def vrrp_transmit_request_handler(self, ev):
        self.logger.debug('send')
        try:
            self.packet_socket.sendto(ev.data,
                                      (self.interface.device_name, 0))
        except:
            self.logger.error('send failed')

    def _initialize(self):
        # nothing
        pass

    def _shutdown(self):
        self.__is_active = False