1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
21 from ryu.controller import handler
22 from ryu.ofproto import ether
23 from ryu.ofproto import inet
24 from ryu.lib import addrconv
25 from ryu.lib import hub
26 from ryu.lib.packet import arp
27 from ryu.lib.packet import vrrp
28 from ryu.services.protocols.vrrp import monitor
29 from ryu.services.protocols.vrrp import event as vrrp_event
30 from ryu.services.protocols.vrrp import utils
33 # Those are not defined in socket module
36 MCAST_LEAVE_GROUP = 45
37 PACKET_ADD_MEMBERSHIP = 1
38 PACKET_DROP_MEMBERSHIP = 2
39 PACKET_MR_MULTICAST = 0
43 def if_nametoindex(ifname):
44 filename = '/sys/class/net/' + ifname + '/ifindex'
45 with contextlib.closing(open(filename)) as f:
50 @monitor.VRRPInterfaceMonitor.register(vrrp_event.VRRPInterfaceNetworkDevice)
51 class VRRPInterfaceMonitorNetworkDevice(monitor.VRRPInterfaceMonitor):
53 This module uses raw socket so that privilege(CAP_NET_ADMIN capability)
57 def __init__(self, *args, **kwargs):
58 super(VRRPInterfaceMonitorNetworkDevice, self).__init__(*args,
60 self.__is_active = True
63 family = socket.AF_INET6
64 ether_type = ether.ETH_TYPE_IPV6
65 mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
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,
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)))
81 self.ifindex = if_nametoindex(self.interface.device_name)
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):
90 packet_socket.recv(1500)
93 packet_socket.setblocking(1)
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))
101 self.__is_active = False
102 super(VRRPInterfaceMonitorNetworkDevice, self).stop()
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):
109 mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
111 mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
113 add_drop = PACKET_ADD_MEMBERSHIP
115 add_drop = PACKET_DROP_MEMBERSHIP
116 # struct packet_mreq {
118 # unsigned short mr_type;
119 # unsigned short mr_alen;
120 # unsigned char mr_mr_address[8];
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)
127 def _join_vrrp_group(self, join_leave):
129 join_leave = MCAST_JOIN_GROUP
131 join_leave = MCAST_LEAVE_GROUP
134 # __u32 gr_interface; /* interface index */
135 # struct __kernel_sockaddr_storage gr_group; /* group address */
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) */
149 # unsigned char s6_addr[16]; /* IPv6 address */
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)
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)];
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)
175 sockaddr += b'\x00' * (SS_MAXSIZE - len(sockaddr))
176 group_req += sockaddr
178 self.ip_socket.setsockopt(family, join_leave, group_req)
181 def _recv_loop(self):
182 packet_socket = self.packet_socket
183 packet_socket.settimeout(1.3) # to check activeness periodically
185 while self.__is_active:
187 buf = packet_socket.recv(128)
188 except socket.timeout:
189 self.logger.debug('timeout')
192 self.logger.error('recv failed')
195 self.__is_active = False
198 self.logger.debug('recv buf')
199 self._send_vrrp_packet_received(buf)
201 self._join_vrrp_group(False)
202 self._join_multicast_membership(False)
204 @handler.set_ev_handler(vrrp_event.EventVRRPTransmitRequest)
205 def vrrp_transmit_request_handler(self, ev):
206 self.logger.debug('send')
208 self.packet_socket.sendto(ev.data,
209 (self.interface.device_name, 0))
211 self.logger.error('send failed')
213 def _initialize(self):
218 self.__is_active = False