9450f770cc9207f885fdf60d2231d83872b38fd5
[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                                             List<NodeConnectorRef> outportList, RouterSolicitationPacket rsPdu) {
54         if (pktService == null) {
55             LOG.info("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         for (NodeConnectorRef outport: outportList) {
63             InstanceIdentifier<Node> outNode = outport.getValue().firstIdentifierOf(Node.class);
64             TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(txPayload)
65                     .setNode(new NodeRef(outNode))
66                     .setEgress(outport).build();
67             LOG.debug("Transmitting the Router Advt packet out {}", outport);
68             pktService.transmitPacket(input);
69         }
70         return true;
71     }
72
73     private void updateRAResponse(Ipv6RtrAdvertType raType, RouterSolicitationPacket pdu,
74                                   RouterAdvertisementPacketBuilder raPacket,
75                                   VirtualPort routerPort) {
76         short icmpv6RaFlags = 0;
77         String gatewayMac = null;
78         IpAddress gatewayIp;
79         List<String> autoConfigPrefixList = new ArrayList<String>();
80         List<String> statefulConfigPrefixList = new ArrayList<String>();
81
82         for (VirtualSubnet subnet : routerPort.getSubnets()) {
83             gatewayIp = subnet.getGatewayIp();
84             // Skip if its a v4 subnet.
85             if (gatewayIp.getIpv4Address() != null) {
86                 continue;
87             }
88
89             if (!subnet.getIpv6RAMode().isEmpty()) {
90                 if (Ipv6Constants.IPV6_AUTO_ADDRESS_SUBNETS.contains(subnet.getIpv6RAMode())) {
91                     autoConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
92                 }
93
94                 if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
95                     statefulConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
96                 }
97             }
98
99             if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATELESS)) {
100                 icmpv6RaFlags = (short) (icmpv6RaFlags | (1 << 6)); // Other Configuration.
101             } else if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
102                 icmpv6RaFlags = (short) (icmpv6RaFlags | (1 << 7)); // Managed Address Conf.
103             }
104         }
105
106         gatewayMac = routerPort.getMacAddress();
107
108         MacAddress sourceMac = MacAddress.getDefaultInstance(gatewayMac);
109         raPacket.setSourceMac(sourceMac);
110         if (raType == Ipv6RtrAdvertType.SOLICITED_ADVERTISEMENT) {
111             raPacket.setDestinationMac(pdu.getSourceMac());
112             raPacket.setDestinationIpv6(pdu.getSourceIpv6());
113             raPacket.setFlowLabel(pdu.getFlowLabel());
114         } else {
115             raPacket.setDestinationMac(new MacAddress(Ipv6Constants.DEF_MCAST_MAC));
116             raPacket.setDestinationIpv6(Ipv6ServiceUtils.ALL_NODES_MCAST_ADDR);
117             raPacket.setFlowLabel(Ipv6Constants.DEF_FLOWLABEL);
118         }
119
120         raPacket.setEthertype(Ipv6Constants.IP_V6_ETHTYPE);
121
122         raPacket.setVersion(Ipv6Constants.IPV6_VERSION);
123         int prefixListLength = autoConfigPrefixList.size() + statefulConfigPrefixList.size();
124         raPacket.setIpv6Length(Ipv6Constants.ICMPV6_RA_LENGTH_WO_OPTIONS
125                 + Ipv6Constants.ICMPV6_OPTION_SOURCE_LLA_LENGTH
126                 + prefixListLength * Ipv6Constants.ICMPV6_OPTION_PREFIX_LENGTH);
127         raPacket.setNextHeader(Ipv6Constants.ICMP6_NHEADER);
128         raPacket.setHopLimit(Ipv6Constants.ICMP_V6_MAX_HOP_LIMIT);
129         raPacket.setSourceIpv6(ipv6Utils.getIpv6LinkLocalAddressFromMac(sourceMac));
130
131         raPacket.setIcmp6Type(Ipv6Constants.ICMP_V6_RA_CODE);
132         raPacket.setIcmp6Code((short)0);
133         raPacket.setIcmp6Chksum(0);
134
135         raPacket.setCurHopLimit((short) Ipv6Constants.IPV6_DEFAULT_HOP_LIMIT);
136         raPacket.setFlags((short) icmpv6RaFlags);
137
138         if (raType == Ipv6RtrAdvertType.CEASE_ADVERTISEMENT) {
139             raPacket.setRouterLifetime(0);
140         } else {
141             raPacket.setRouterLifetime(Ipv6Constants.IPV6_ROUTER_LIFETIME);
142         }
143         raPacket.setReachableTime((long) Ipv6Constants.IPV6_RA_REACHABLE_TIME);
144         raPacket.setRetransTime((long) 0);
145
146         raPacket.setOptionSourceAddr((short)1);
147         raPacket.setSourceAddrLength((short)1);
148         raPacket.setSourceLlAddress(MacAddress.getDefaultInstance(gatewayMac));
149
150         List<PrefixList> prefixList = new ArrayList<PrefixList>();
151         PrefixListBuilder prefix = new PrefixListBuilder();
152         prefix.setOptionType((short)3);
153         prefix.setOptionLength((short)4);
154         // Note: EUI-64 auto-configuration requires 64 bits.
155         prefix.setPrefixLength((short)64);
156         prefix.setValidLifetime((long) Ipv6Constants.IPV6_RA_VALID_LIFETIME);
157         prefix.setPreferredLifetime((long) Ipv6Constants.IPV6_RA_PREFERRED_LIFETIME);
158         prefix.setReserved((long) 0);
159
160         short autoConfPrefixFlags = 0;
161         autoConfPrefixFlags = (short) (autoConfPrefixFlags | (1 << 7)); // On-link flag
162         autoConfPrefixFlags = (short) (autoConfPrefixFlags | (1 << 6)); // Autonomous address-configuration flag.
163         for (String v6Prefix : autoConfigPrefixList) {
164             prefix.setFlags((short)autoConfPrefixFlags);
165             prefix.setPrefix(new Ipv6Prefix(v6Prefix));
166             prefixList.add(prefix.build());
167         }
168
169         short statefulPrefixFlags = 0;
170         statefulPrefixFlags = (short) (statefulPrefixFlags | (1 << 7)); // On-link flag
171         for (String v6Prefix : statefulConfigPrefixList) {
172             prefix.setFlags((short)statefulPrefixFlags);
173             prefix.setPrefix(new Ipv6Prefix(v6Prefix));
174             prefixList.add(prefix.build());
175         }
176
177         raPacket.setPrefixList((List<PrefixList>) prefixList);
178
179         return;
180     }
181
182     private byte[] fillRouterAdvertisementPacket(RouterAdvertisementPacket pdu) {
183         ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
184
185         buf.put(ipv6Utils.convertEthernetHeaderToByte((EthernetHeader)pdu), 0, 14);
186         buf.put(ipv6Utils.convertIpv6HeaderToByte((Ipv6Header)pdu), 0, 40);
187         buf.put(icmp6RAPayloadtoByte(pdu), 0, pdu.getIpv6Length());
188         int checksum = ipv6Utils.calcIcmpv6Checksum(buf.array(), (Ipv6Header) pdu);
189         buf.putShort((Ipv6Constants.ICMPV6_OFFSET + 2), (short)checksum);
190         return (buf.array());
191     }
192
193     private byte[] icmp6RAPayloadtoByte(RouterAdvertisementPacket pdu) {
194         byte[] data = new byte[pdu.getIpv6Length()];
195         Arrays.fill(data, (byte)0);
196
197         ByteBuffer buf = ByteBuffer.wrap(data);
198         buf.put((byte)pdu.getIcmp6Type().shortValue());
199         buf.put((byte)pdu.getIcmp6Code().shortValue());
200         buf.putShort((short)pdu.getIcmp6Chksum().intValue());
201         buf.put((byte)pdu.getCurHopLimit().shortValue());
202         buf.put((byte)pdu.getFlags().shortValue());
203         buf.putShort((short)pdu.getRouterLifetime().intValue());
204         buf.putInt((int)pdu.getReachableTime().longValue());
205         buf.putInt((int)pdu.getRetransTime().longValue());
206         buf.put((byte)pdu.getOptionSourceAddr().shortValue());
207         buf.put((byte)pdu.getSourceAddrLength().shortValue());
208         buf.put(ipv6Utils.bytesFromHexString(pdu.getSourceLlAddress().getValue().toString()));
209
210         for (PrefixList prefix : pdu.getPrefixList()) {
211             buf.put((byte)prefix.getOptionType().shortValue());
212             buf.put((byte)prefix.getOptionLength().shortValue());
213             buf.put((byte)prefix.getPrefixLength().shortValue());
214             buf.put((byte)prefix.getFlags().shortValue());
215             buf.putInt((int)prefix.getValidLifetime().longValue());
216             buf.putInt((int)prefix.getPreferredLifetime().longValue());
217             buf.putInt((int)prefix.getReserved().longValue());
218             buf.put(IetfInetUtil.INSTANCE.ipv6PrefixToBytes(new Ipv6Prefix(prefix.getPrefix())),0,16);
219         }
220         return data;
221     }
222 }