Refactor Ipv6Service module.
[netvirt.git] / 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.math.BigInteger;
12 import java.nio.ByteBuffer;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.List;
16 import org.opendaylight.genius.ipv6util.api.Icmpv6Type;
17 import org.opendaylight.genius.ipv6util.api.Ipv6Constants;
18 import org.opendaylight.genius.ipv6util.api.Ipv6Constants.Ipv6RouterAdvertisementType;
19 import org.opendaylight.genius.ipv6util.api.Ipv6Util;
20 import org.opendaylight.genius.mdsalutil.ActionInfo;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
24 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
25 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
26 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceConstants;
27 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.RouterAdvertisementPacket;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.RouterAdvertisementPacketBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.RouterSolicitationPacket;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.router.advertisement.packet.PrefixList;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.packet.rev160620.router.advertisement.packet.PrefixListBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45 public class Ipv6RouterAdvt {
46     private static final Logger LOG = LoggerFactory.getLogger(Ipv6RouterAdvt.class);
47     private final PacketProcessingService packetService;
48     private final IfMgr ifMgr;
49
50     public Ipv6RouterAdvt(PacketProcessingService packetService, IfMgr ifMgr) {
51         this.packetService = packetService;
52         this.ifMgr = ifMgr;
53     }
54
55     public boolean transmitRtrAdvertisement(Ipv6RouterAdvertisementType raType, VirtualPort routerPort,
56                                             long elanTag, RouterSolicitationPacket rsPdu,
57                                             BigInteger dpnId, Uuid port) {
58         RouterAdvertisementPacketBuilder raPacket = new RouterAdvertisementPacketBuilder();
59         updateRAResponse(raType, rsPdu, raPacket, routerPort);
60         // Serialize the response packet
61         byte[] txPayload = fillRouterAdvertisementPacket(raPacket.build());
62         TransmitPacketInput input = null;
63         /* Send solicited router advertisement to requested VM port only.
64          * Send periodic unsolicited router advertisement to ELAN broadcast group.
65          */
66         if (raType == Ipv6RouterAdvertisementType.SOLICITED_ADVERTISEMENT) {
67             List<Action> actions = ifMgr.getEgressAction(port.getValue());
68             if (actions == null || actions.isEmpty()) {
69                 LOG.error("Unable to send solicited router advertisement packet out. Since Egress "
70                         + "action is empty for interface {}. ", port.getValue());
71                 return false;
72             }
73             input = MDSALUtil.getPacketOut(actions, txPayload, dpnId);
74             LOG.debug("Transmitting the Router Advt packet out to port {}", port.getValue());
75         } else {
76             /* Here we handle UNSOLICITED_ADVERTISEMENT, CEASE_ADVERTISEMENT */
77             long elanGroupId = Ipv6ServiceUtils.getRemoteBCGroup(elanTag);
78             List<ActionInfo> lstActionInfo = new ArrayList<>();
79             lstActionInfo.add(new ActionGroup(elanGroupId));
80             input = MDSALUtil.getPacketOutDefault(lstActionInfo, txPayload, dpnId);
81             LOG.debug("Transmitting the Router Advt packet out to ELAN Group ID {}", elanGroupId);
82         }
83         JdkFutures.addErrorLogging(packetService.transmitPacket(input), LOG, "transmitPacket");
84         return true;
85     }
86
87     private void updateRAResponse(Ipv6RouterAdvertisementType raType, RouterSolicitationPacket pdu,
88                                   RouterAdvertisementPacketBuilder raPacket,
89                                   VirtualPort routerPort) {
90         short icmpv6RaFlags = 0;
91         String gatewayMac = null;
92         IpAddress gatewayIp;
93         List<String> autoConfigPrefixList = new ArrayList<>();
94         List<String> statefulConfigPrefixList = new ArrayList<>();
95
96         for (VirtualSubnet subnet : routerPort.getSubnets()) {
97             gatewayIp = subnet.getGatewayIp();
98             // Skip if its a v4 subnet.
99             if (gatewayIp.getIpv4Address() != null) {
100                 continue;
101             }
102
103             if (!subnet.getIpv6RAMode().isEmpty()) {
104                 if (Ipv6ServiceConstants.IPV6_AUTO_ADDRESS_SUBNETS.contains(subnet.getIpv6RAMode())) {
105                     autoConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
106                 }
107
108                 if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6ServiceConstants.IPV6_DHCPV6_STATEFUL)) {
109                     statefulConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
110                 }
111             }
112
113             if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6ServiceConstants.IPV6_DHCPV6_STATELESS)) {
114                 icmpv6RaFlags = (short) (icmpv6RaFlags | 1 << 6); // Other Configuration.
115             } else if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6ServiceConstants.IPV6_DHCPV6_STATEFUL)) {
116                 icmpv6RaFlags = (short) (icmpv6RaFlags | 1 << 7); // Managed Address Conf.
117             }
118         }
119
120         gatewayMac = routerPort.getMacAddress();
121
122         MacAddress sourceMac = MacAddress.getDefaultInstance(gatewayMac);
123         raPacket.setSourceMac(sourceMac);
124         if (raType == Ipv6RouterAdvertisementType.SOLICITED_ADVERTISEMENT) {
125             raPacket.setDestinationMac(pdu.getSourceMac());
126             raPacket.setDestinationIpv6(pdu.getSourceIpv6());
127             raPacket.setFlowLabel(pdu.getFlowLabel());
128         } else {
129             raPacket.setDestinationMac(new MacAddress(Ipv6Constants.ALL_NODES_MCAST_MAC));
130             raPacket.setDestinationIpv6(Ipv6ServiceUtils.ALL_NODES_MCAST_ADDR);
131             raPacket.setFlowLabel(Ipv6ServiceConstants.DEF_FLOWLABEL);
132         }
133
134         raPacket.setEthertype(NwConstants.ETHTYPE_IPV6);
135
136         raPacket.setVersion(Ipv6Constants.IPV6_VERSION);
137         int prefixListLength = autoConfigPrefixList.size() + statefulConfigPrefixList.size();
138         raPacket.setIpv6Length(Ipv6Constants.ICMPV6_RA_LENGTH_WO_OPTIONS
139                 + Ipv6Constants.ICMPV6_OPTION_SOURCE_LLA_LENGTH
140                 + prefixListLength * Ipv6Constants.ICMPV6_OPTION_PREFIX_LENGTH);
141         raPacket.setNextHeader(IPProtocols.IPV6ICMP.shortValue());
142         raPacket.setHopLimit(Ipv6Constants.ICMP_V6_MAX_HOP_LIMIT);
143         raPacket.setSourceIpv6(Ipv6Util.getIpv6LinkLocalAddressFromMac(sourceMac));
144
145         raPacket.setIcmp6Type(Icmpv6Type.ROUTER_ADVETISEMENT.getValue());
146         raPacket.setIcmp6Code((short)0);
147         raPacket.setIcmp6Chksum(0);
148
149         raPacket.setCurHopLimit((short) Ipv6Constants.IPV6_DEFAULT_HOP_LIMIT);
150         raPacket.setFlags(icmpv6RaFlags);
151
152         if (raType == Ipv6RouterAdvertisementType.CEASE_ADVERTISEMENT) {
153             raPacket.setRouterLifetime(0);
154         } else {
155             raPacket.setRouterLifetime(Ipv6Constants.IPV6_ROUTER_LIFETIME);
156         }
157         raPacket.setReachableTime((long) Ipv6Constants.IPV6_RA_REACHABLE_TIME);
158         raPacket.setRetransTime((long) 0);
159
160         raPacket.setOptionSourceAddr((short)1);
161         raPacket.setSourceAddrLength((short)1);
162         raPacket.setSourceLlAddress(MacAddress.getDefaultInstance(gatewayMac));
163
164         List<PrefixList> prefixList = new ArrayList<>();
165         PrefixListBuilder prefix = new PrefixListBuilder();
166         prefix.setOptionType((short)3);
167         prefix.setOptionLength((short)4);
168         // Note: EUI-64 auto-configuration requires 64 bits.
169         prefix.setPrefixLength((short)64);
170         prefix.setValidLifetime((long) Ipv6Constants.IPV6_RA_VALID_LIFETIME);
171         prefix.setPreferredLifetime((long) Ipv6Constants.IPV6_RA_PREFERRED_LIFETIME);
172         prefix.setReserved((long) 0);
173
174         short autoConfPrefixFlags = 0;
175         autoConfPrefixFlags = (short) (autoConfPrefixFlags | 1 << 7); // On-link flag
176         autoConfPrefixFlags = (short) (autoConfPrefixFlags | 1 << 6); // Autonomous address-configuration flag.
177         for (String v6Prefix : autoConfigPrefixList) {
178             prefix.setFlags(autoConfPrefixFlags);
179             prefix.setPrefix(new Ipv6Prefix(v6Prefix));
180             prefixList.add(prefix.build());
181         }
182
183         short statefulPrefixFlags = 0;
184         statefulPrefixFlags = (short) (statefulPrefixFlags | 1 << 7); // On-link flag
185         for (String v6Prefix : statefulConfigPrefixList) {
186             prefix.setFlags(statefulPrefixFlags);
187             prefix.setPrefix(new Ipv6Prefix(v6Prefix));
188             prefixList.add(prefix.build());
189         }
190
191         raPacket.setPrefixList(prefixList);
192
193         return;
194     }
195
196     private byte[] fillRouterAdvertisementPacket(RouterAdvertisementPacket pdu) {
197         ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
198
199         buf.put(Ipv6Util.convertEthernetHeaderToByte(pdu), 0, 14);
200         buf.put(Ipv6Util.convertIpv6HeaderToByte(pdu), 0, 40);
201         buf.put(icmp6RAPayloadtoByte(pdu), 0, pdu.getIpv6Length());
202         int checksum = Ipv6Util.calculateIcmpv6Checksum(buf.array(), pdu);
203         buf.putShort(Ipv6Constants.ICMPV6_OFFSET + 2, (short)checksum);
204         return buf.array();
205     }
206
207     private byte[] icmp6RAPayloadtoByte(RouterAdvertisementPacket pdu) {
208         byte[] data = new byte[pdu.getIpv6Length()];
209         Arrays.fill(data, (byte)0);
210
211         ByteBuffer buf = ByteBuffer.wrap(data);
212         buf.put((byte)pdu.getIcmp6Type().shortValue());
213         buf.put((byte)pdu.getIcmp6Code().shortValue());
214         buf.putShort((short)pdu.getIcmp6Chksum().intValue());
215         buf.put((byte)pdu.getCurHopLimit().shortValue());
216         buf.put((byte)pdu.getFlags().shortValue());
217         buf.putShort((short)pdu.getRouterLifetime().intValue());
218         buf.putInt((int)pdu.getReachableTime().longValue());
219         buf.putInt((int)pdu.getRetransTime().longValue());
220         buf.put((byte)pdu.getOptionSourceAddr().shortValue());
221         buf.put((byte)pdu.getSourceAddrLength().shortValue());
222         buf.put(Ipv6Util.bytesFromHexString(pdu.getSourceLlAddress().getValue()));
223
224         for (PrefixList prefix : pdu.getPrefixList()) {
225             buf.put((byte)prefix.getOptionType().shortValue());
226             buf.put((byte)prefix.getOptionLength().shortValue());
227             buf.put((byte)prefix.getPrefixLength().shortValue());
228             buf.put((byte)prefix.getFlags().shortValue());
229             buf.putInt((int)prefix.getValidLifetime().longValue());
230             buf.putInt((int)prefix.getPreferredLifetime().longValue());
231             buf.putInt((int)prefix.getReserved().longValue());
232             buf.put(IetfInetUtil.INSTANCE.ipv6PrefixToBytes(new Ipv6Prefix(prefix.getPrefix())),0,16);
233         }
234         return data;
235     }
236 }