1# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2# Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import contextlib
18import socket
19import struct
20
21from ryu.controller import handler
22from ryu.ofproto import ether
23from ryu.ofproto import inet
24from ryu.lib import addrconv
25from ryu.lib import hub
26from ryu.lib.packet import arp
27from ryu.lib.packet import vrrp
28from ryu.services.protocols.vrrp import monitor
29from ryu.services.protocols.vrrp import event as vrrp_event
30from ryu.services.protocols.vrrp import utils
31
32
33# Those are not defined in socket module
34SS_MAXSIZE = 128
35MCAST_JOIN_GROUP = 42
36MCAST_LEAVE_GROUP = 45
37PACKET_ADD_MEMBERSHIP = 1
38PACKET_DROP_MEMBERSHIP = 2
39PACKET_MR_MULTICAST = 0
40SOL_PACKET = 263
41
42
43def if_nametoindex(ifname):
44    filename = '/sys/class/net/' + ifname + '/ifindex'
45    with contextlib.closing(open(filename)) as f:
46        for line in f:
47            return int(line)
48
49
50@monitor.VRRPInterfaceMonitor.register(vrrp_event.VRRPInterfaceNetworkDevice)
51class VRRPInterfaceMonitorNetworkDevice(monitor.VRRPInterfaceMonitor):
52    """
53    This module uses raw socket so that privilege(CAP_NET_ADMIN capability)
54    is required.
55    """
56
57    def __init__(self, *args, **kwargs):
58        super(VRRPInterfaceMonitorNetworkDevice, self).__init__(*args,
59                                                                **kwargs)
60        self.__is_active = True
61        config = self.config
62        if config.is_ipv6:
63            family = socket.AF_INET6
64            ether_type = ether.ETH_TYPE_IPV6
65            mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
66        else:
67            family = socket.AF_INET
68            ether_type = ether.ETH_TYPE_IP
69            mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
70        # socket module doesn't define IPPROTO_VRRP
71        self.ip_socket = socket.socket(family, socket.SOCK_RAW,
72                                       inet.IPPROTO_VRRP)
73
74        self.packet_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
75                                           socket.htons(ether_type))
76        self.packet_socket.bind((self.interface.device_name, ether_type,
77                                 socket.PACKET_MULTICAST,
78                                 arp.ARP_HW_TYPE_ETHERNET,
79                                 addrconv.mac.text_to_bin(mac_address)))
80
81        self.ifindex = if_nametoindex(self.interface.device_name)
82
83    def start(self):
84        # discard received packets before joining multicast membership
85        packet_socket = self.packet_socket
86        packet_socket.setblocking(0)
87        with hub.Timeout(0.1, False):
88            while True:
89                try:
90                    packet_socket.recv(1500)
91                except socket.error:
92                    break
93        packet_socket.setblocking(1)
94
95        self._join_multicast_membership(True)
96        self._join_vrrp_group(True)
97        super(VRRPInterfaceMonitorNetworkDevice, self).start()
98        self.threads.append(hub.spawn(self._recv_loop))
99
100    def stop(self):
101        self.__is_active = False
102        super(VRRPInterfaceMonitorNetworkDevice, self).stop()
103
104    # we assume that the structures in the following two functions for
105    # multicast are aligned in the same way on all the archtectures.
106    def _join_multicast_membership(self, join_leave):
107        config = self.config
108        if config.is_ipv6:
109            mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
110        else:
111            mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
112        if join_leave:
113            add_drop = PACKET_ADD_MEMBERSHIP
114        else:
115            add_drop = PACKET_DROP_MEMBERSHIP
116        # struct packet_mreq {
117        #     int mr_ifindex;
118        #     unsigned short mr_type;
119        #     unsigned short mr_alen;
120        #     unsigned char  mr_mr_address[8];
121        # };
122        packet_mreq = struct.pack('IHH8s', self.ifindex,
123                                  PACKET_MR_MULTICAST, 6,
124                                  addrconv.mac.text_to_bin(mac_address))
125        self.packet_socket.setsockopt(SOL_PACKET, add_drop, packet_mreq)
126
127    def _join_vrrp_group(self, join_leave):
128        if join_leave:
129            join_leave = MCAST_JOIN_GROUP
130        else:
131            join_leave = MCAST_LEAVE_GROUP
132
133        # struct group_req {
134        #     __u32 gr_interface;  /* interface index */
135        #     struct __kernel_sockaddr_storage gr_group; /* group address */
136        # };
137        group_req = struct.pack('I', self.ifindex)
138        # padding to gr_group. This is environment dependent
139        group_req += b'\x00' * (struct.calcsize('P') - struct.calcsize('I'))
140        if self.config.is_ipv6:
141            # struct sockaddr_in6 {
142            #     sa_family_t     sin6_family;   /* AF_INET6 */
143            #     in_port_t       sin6_port;     /* port number */
144            #     uint32_t        sin6_flowinfo; /* IPv6 flow information */
145            #     struct in6_addr sin6_addr;     /* IPv6 address */
146            #     uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
147            # };
148            # struct in6_addr {
149            #     unsigned char   s6_addr[16];   /* IPv6 address */
150            # };
151            family = socket.IPPROTO_IPV6
152            sockaddr = struct.pack('H', socket.AF_INET6)
153            sockaddr += struct.pack('!H', 0)
154            sockaddr += struct.pack('!I', 0)
155            sockaddr += addrconv.ipv6.text_to_bin(vrrp.VRRP_IPV6_DST_ADDRESS)
156            sockaddr += struct.pack('I', 0)
157        else:
158            # #define __SOCK_SIZE__   16 /* sizeof(struct sockaddr) */
159            # struct sockaddr_in {
160            #   __kernel_sa_family_t  sin_family;     /* Address family */
161            #   __be16                sin_port;       /* Port number */
162            #   struct in_addr        sin_addr;       /* Internet address */
163            #   /* Pad to size of `struct sockaddr'. */
164            #   unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -
165            #           sizeof(unsigned short int) - sizeof(struct in_addr)];
166            # };
167            # struct in_addr {
168            #     __be32  s_addr;
169            # };
170            family = socket.IPPROTO_IP
171            sockaddr = struct.pack('H', socket.AF_INET)
172            sockaddr += struct.pack('!H', 0)
173            sockaddr += addrconv.ipv4.text_to_bin(vrrp.VRRP_IPV4_DST_ADDRESS)
174
175        sockaddr += b'\x00' * (SS_MAXSIZE - len(sockaddr))
176        group_req += sockaddr
177
178        self.ip_socket.setsockopt(family, join_leave, group_req)
179        return
180
181    def _recv_loop(self):
182        packet_socket = self.packet_socket
183        packet_socket.settimeout(1.3)       # to check activeness periodically
184        try:
185            while self.__is_active:
186                try:
187                    buf = packet_socket.recv(128)
188                except socket.timeout:
189                    self.logger.debug('timeout')
190                    continue
191                except:
192                    self.logger.error('recv failed')
193                    continue
194                if len(buf) == 0:
195                    self.__is_active = False
196                    break
197
198                self.logger.debug('recv buf')
199                self._send_vrrp_packet_received(buf)
200        finally:
201            self._join_vrrp_group(False)
202            self._join_multicast_membership(False)
203
204    @handler.set_ev_handler(vrrp_event.EventVRRPTransmitRequest)
205    def vrrp_transmit_request_handler(self, ev):
206        self.logger.debug('send')
207        try:
208            self.packet_socket.sendto(ev.data,
209                                      (self.interface.device_name, 0))
210        except:
211            self.logger.error('send failed')
212
213    def _initialize(self):
214        # nothing
215        pass
216
217    def _shutdown(self):
218        self.__is_active = False
219