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