Merge "Periodic RA transmission"
[netvirt.git] / vpnservice / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / Ipv6RouterAdvt.java
1 /*
2  * Copyright (c) 2016 Dell Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.netvirt.ipv6service;
10
11 import java.nio.ByteBuffer;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.List;
15 import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
16 import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants.Ipv6RtrAdvertType;
17 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterAdvertisementPacket;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterAdvertisementPacketBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterSolicitationPacket;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.router.advertisement.packet.PrefixList;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.router.advertisement.packet.PrefixListBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 public class Ipv6RouterAdvt {
40     private static final Logger LOG = LoggerFactory.getLogger(Ipv6RouterAdvt.class);
41     private static PacketProcessingService pktService;
42     private Ipv6ServiceUtils ipv6Utils;
43
44     public Ipv6RouterAdvt() {
45         ipv6Utils = Ipv6ServiceUtils.getInstance();
46     }
47
48     public static void setPacketProcessingService(PacketProcessingService packetService) {
49         pktService = packetService;
50     }
51
52     public boolean transmitRtrAdvertisement(Ipv6RtrAdvertType raType, VirtualPort routerPort,
53                                             NodeConnectorRef outport, RouterSolicitationPacket rsPdu) {
54         if (pktService == null) {
55             LOG.debug("transmitRtrAdvertisement packet processing service is not yet configured");
56             return false;
57         }
58         RouterAdvertisementPacketBuilder raPacket = new RouterAdvertisementPacketBuilder();
59         updateRAResponse(raType, rsPdu, raPacket, routerPort);
60         // Serialize the response packet
61         byte[] txPayload = fillRouterAdvertisementPacket(raPacket.build());
62         InstanceIdentifier<Node> outNode = outport.getValue().firstIdentifierOf(Node.class);
63         TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(txPayload)
64                 .setNode(new NodeRef(outNode))
65                 .setEgress(outport).build();
66         LOG.debug("Transmitting the Router Advt packet out {}", outport);
67         pktService.transmitPacket(input);
68         return true;
69     }
70
71     private void updateRAResponse(Ipv6RtrAdvertType raType, RouterSolicitationPacket pdu,
72                                   RouterAdvertisementPacketBuilder raPacket,
73                                   VirtualPort routerPort) {
74         short icmpv6RaFlags = 0;
75         String gatewayMac = null;
76         IpAddress gatewayIp;
77         List<String> autoConfigPrefixList = new ArrayList<String>();
78         List<String> statefulConfigPrefixList = new ArrayList<String>();
79
80         for (VirtualSubnet subnet : routerPort.getSubnets()) {
81             gatewayIp = subnet.getGatewayIp();
82             // Skip if its a v4 subnet.
83             if (gatewayIp.getIpv4Address() != null) {
84                 continue;
85             }
86
87             if (!subnet.getIpv6RAMode().isEmpty()) {
88                 if (Ipv6Constants.IPV6_AUTO_ADDRESS_SUBNETS.contains(subnet.getIpv6RAMode())) {
89                     autoConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
90                 }
91
92                 if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
93                     statefulConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
94                 }
95             }
96
97             if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATELESS)) {
98                 icmpv6RaFlags = (short) (icmpv6RaFlags | (1 << 6)); // Other Configuration.
99             } else if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
100                 icmpv6RaFlags = (short) (icmpv6RaFlags | (1 << 7)); // Managed Address Conf.
101             }
102         }
103
104         gatewayMac = routerPort.getMacAddress();
105
106         MacAddress sourceMac = MacAddress.getDefaultInstance(gatewayMac);
107         raPacket.setSourceMac(sourceMac);
108         if (raType == Ipv6RtrAdvertType.SOLICITED_ADVERTISEMENT) {
109             raPacket.setDestinationMac(pdu.getSourceMac());
110             raPacket.setDestinationIpv6(pdu.getSourceIpv6());
111             raPacket.setFlowLabel(pdu.getFlowLabel());
112         } else {
113             raPacket.setDestinationMac(new MacAddress(Ipv6Constants.DEF_MCAST_MAC));
114             raPacket.setDestinationIpv6(Ipv6ServiceUtils.ALL_NODES_MCAST_ADDR);
115             raPacket.setFlowLabel(Ipv6Constants.DEF_FLOWLABEL);
116         }
117
118         raPacket.setEthertype(Ipv6Constants.IP_V6_ETHTYPE);
119
120         raPacket.setVersion(Ipv6Constants.IPV6_VERSION);
121         int prefixListLength = autoConfigPrefixList.size() + statefulConfigPrefixList.size();
122         raPacket.setIpv6Length(Ipv6Constants.ICMPV6_RA_LENGTH_WO_OPTIONS
123                 + Ipv6Constants.ICMPV6_OPTION_SOURCE_LLA_LENGTH
124                 + prefixListLength * Ipv6Constants.ICMPV6_OPTION_PREFIX_LENGTH);
125         raPacket.setNextHeader(Ipv6Constants.ICMP6_NHEADER);
126         raPacket.setHopLimit(Ipv6Constants.ICMP_V6_MAX_HOP_LIMIT);
127         raPacket.setSourceIpv6(ipv6Utils.getIpv6LinkLocalAddressFromMac(sourceMac));
128
129         raPacket.setIcmp6Type(Ipv6Constants.ICMP_V6_RA_CODE);
130         raPacket.setIcmp6Code((short)0);
131         raPacket.setIcmp6Chksum(0);
132
133         raPacket.setCurHopLimit((short) Ipv6Constants.IPV6_DEFAULT_HOP_LIMIT);
134         raPacket.setFlags((short) icmpv6RaFlags);
135
136         if (raType == Ipv6RtrAdvertType.CEASE_ADVERTISEMENT) {
137             raPacket.setRouterLifetime(0);
138         } else {
139             raPacket.setRouterLifetime(Ipv6Constants.IPV6_ROUTER_LIFETIME);
140         }
141         raPacket.setReachableTime((long) 0);
142         raPacket.setRetransTime((long) 0);
143
144         raPacket.setOptionSourceAddr((short)1);
145         raPacket.setSourceAddrLength((short)1);
146         raPacket.setSourceLlAddress(MacAddress.getDefaultInstance(gatewayMac));
147
148         List<PrefixList> prefixList = new ArrayList<PrefixList>();
149         PrefixListBuilder prefix = new PrefixListBuilder();
150         prefix.setOptionType((short)3);
151         prefix.setOptionLength((short)4);
152         // Note: EUI-64 auto-configuration requires 64 bits.
153         prefix.setPrefixLength((short)64);
154         prefix.setValidLifetime((long) Ipv6Constants.IPV6_RA_VALID_LIFETIME);
155         prefix.setPreferredLifetime((long) Ipv6Constants.IPV6_RA_PREFERRED_LIFETIME);
156         prefix.setReserved((long) 0);
157
158         short autoConfPrefixFlags = 0;
159         autoConfPrefixFlags = (short) (autoConfPrefixFlags | (1 << 7)); // On-link flag
160         autoConfPrefixFlags = (short) (autoConfPrefixFlags | (1 << 6)); // Autonomous address-configuration flag.
161         for (String v6Prefix : autoConfigPrefixList) {
162             prefix.setFlags((short)autoConfPrefixFlags);
163             prefix.setPrefix(new Ipv6Prefix(v6Prefix));
164             prefixList.add(prefix.build());
165         }
166
167         short statefulPrefixFlags = 0;
168         statefulPrefixFlags = (short) (statefulPrefixFlags | (1 << 7)); // On-link flag
169         for (String v6Prefix : statefulConfigPrefixList) {
170             prefix.setFlags((short)statefulPrefixFlags);
171             prefix.setPrefix(new Ipv6Prefix(v6Prefix));
172             prefixList.add(prefix.build());
173         }
174
175         raPacket.setPrefixList((List<PrefixList>) prefixList);
176
177         return;
178     }
179
180     private byte[] fillRouterAdvertisementPacket(RouterAdvertisementPacket pdu) {
181         ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
182
183         buf.put(ipv6Utils.convertEthernetHeaderToByte((EthernetHeader)pdu), 0, 14);
184         buf.put(ipv6Utils.convertIpv6HeaderToByte((Ipv6Header)pdu), 0, 40);
185         buf.put(icmp6RAPayloadtoByte(pdu), 0, pdu.getIpv6Length());
186         int checksum = ipv6Utils.calcIcmpv6Checksum(buf.array(), (Ipv6Header) pdu);
187         buf.putShort((Ipv6Constants.ICMPV6_OFFSET + 2), (short)checksum);
188         return (buf.array());
189     }
190
191     private byte[] icmp6RAPayloadtoByte(RouterAdvertisementPacket pdu) {
192         byte[] data = new byte[pdu.getIpv6Length()];
193         Arrays.fill(data, (byte)0);
194
195         ByteBuffer buf = ByteBuffer.wrap(data);
196         buf.put((byte)pdu.getIcmp6Type().shortValue());
197         buf.put((byte)pdu.getIcmp6Code().shortValue());
198         buf.putShort((short)pdu.getIcmp6Chksum().intValue());
199         buf.put((byte)pdu.getCurHopLimit().shortValue());
200         buf.put((byte)pdu.getFlags().shortValue());
201         buf.putShort((short)pdu.getRouterLifetime().intValue());
202         buf.putInt((int)pdu.getReachableTime().longValue());
203         buf.putInt((int)pdu.getRetransTime().longValue());
204         buf.put((byte)pdu.getOptionSourceAddr().shortValue());
205         buf.put((byte)pdu.getSourceAddrLength().shortValue());
206         buf.put(ipv6Utils.bytesFromHexString(pdu.getSourceLlAddress().getValue().toString()));
207
208         for (PrefixList prefix : pdu.getPrefixList()) {
209             buf.put((byte)prefix.getOptionType().shortValue());
210             buf.put((byte)prefix.getOptionLength().shortValue());
211             buf.put((byte)prefix.getPrefixLength().shortValue());
212             buf.put((byte)prefix.getFlags().shortValue());
213             buf.putInt((int)prefix.getValidLifetime().longValue());
214             buf.putInt((int)prefix.getPreferredLifetime().longValue());
215             buf.putInt((int)prefix.getReserved().longValue());
216             buf.put(IetfInetUtil.INSTANCE.ipv6PrefixToBytes(new Ipv6Prefix(prefix.getPrefix())),0,16);
217         }
218         return data;
219     }
220 }