2 * Copyright (c) 2016 Cisco and/or its affiliates.
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 implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress;
19 import static com.google.common.base.Preconditions.checkArgument;
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
24 import io.fd.honeycomb.translate.write.WriteContext;
25 import io.fd.honeycomb.translate.write.WriteFailedException;
26 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
27 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
28 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
29 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
30 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
31 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
32 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
33 import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
34 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.ListIterator;
39 import java.util.concurrent.CompletionStage;
40 import java.util.function.Predicate;
41 import java.util.stream.Collectors;
42 import java.util.stream.Stream;
43 import javax.annotation.Nonnegative;
44 import javax.annotation.Nonnull;
45 import javax.annotation.Nullable;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
65 public final class IetfAclWriter implements JvppReplyConsumer, AclTranslator {
67 private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class);
68 private static final int NOT_DEFINED = -1;
69 private final FutureJVppCore jvpp;
71 private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>();
73 public IetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) {
74 this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null");
75 aceWriters.put(AclType.ETH, new AceEthWriter());
76 aceWriters.put(AclType.IP4, new AceIp4Writer());
77 aceWriters.put(AclType.IP6, new AceIp6Writer());
78 aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter());
81 private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl,
82 @Nonnull final WriteContext writeContext) {
83 final String aclName = assignedAcl.getName();
84 final Class<? extends AclBase> aclType = assignedAcl.getType();
86 // ietf-acl updates are handled first, so we use writeContext.readAfter
87 final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl>
89 writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child(
90 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class,
91 new AclKey(aclName, aclType)));
92 checkArgument(aclOptional.isPresent(), "Acl lists not configured");
93 final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl
94 acl = aclOptional.get();
96 final AccessListEntries accessListEntries = acl.getAccessListEntries();
97 checkArgument(accessListEntries != null, "access list entries not configured");
99 return accessListEntries.getAce().stream();
102 void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex)
103 throws WriteFailedException {
104 final ClassifyTableByInterface request = new ClassifyTableByInterface();
105 request.swIfIndex = swIfIndex;
107 final CompletionStage<ClassifyTableByInterfaceReply> cs = jvpp.classifyTableByInterface(request);
108 final ClassifyTableByInterfaceReply reply = getReplyForDelete(cs.toCompletableFuture(), id);
110 // We unassign and remove all ACL-related classify tables for given interface (we assume we are the only
111 // classify table manager)
113 unassignClassifyTables(id, reply);
115 removeClassifyTable(id, reply.l2TableId);
116 removeClassifyTable(id, reply.ip4TableId);
117 removeClassifyTable(id, reply.ip6TableId);
120 private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id,
121 final ClassifyTableByInterfaceReply currentState)
122 throws WriteFailedException {
123 final InputAclSetInterface request = new InputAclSetInterface();
125 request.swIfIndex = currentState.swIfIndex;
126 request.l2TableIndex = currentState.l2TableId;
127 request.ip4TableIndex = currentState.ip4TableId;
128 request.ip6TableIndex = currentState.ip6TableId;
129 final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
130 jvpp.inputAclSetInterface(request);
131 getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
134 private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex)
135 throws WriteFailedException {
137 if (tableIndex == -1) {
138 return; // classify table id is absent
140 final ClassifyAddDelTable request = new ClassifyAddDelTable();
141 request.tableIndex = tableIndex;
142 final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
143 getReplyForDelete(cs.toCompletableFuture(), id);
146 void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls,
147 final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode,
148 @Nonnull final WriteContext writeContext)
149 throws WriteFailedException {
150 write(id, swIfIndex, mode, acls, defaultAction, writeContext, 0);
153 private static boolean appliesToIp4Path(final Ace ace) {
154 final AceType aceType = ace.getMatches().getAceType();
155 final AclType aclType = AclType.fromAce(ace);
156 if (aclType == AclType.IP4) {
159 if (aclType == AclType.ETH) {
160 return true; // L2 only rules are possible for IP4 traffic
162 if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType)
163 .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) {
169 private static boolean appliesToIp6Path(final Ace ace) {
170 final AceType aceType = ace.getMatches().getAceType();
171 final AclType aclType = AclType.fromAce(ace);
172 if (aclType == AclType.IP6) {
175 if (aclType == AclType.ETH) {
176 return true; // L2 only rules are possible for IP6 traffic
178 if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType)
179 .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) {
185 void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, final InterfaceMode mode,
186 @Nonnull final List<Acl> acls, final AccessLists.DefaultAction defaultAction,
187 @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags)
188 throws WriteFailedException {
189 checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range");
191 final InputAclSetInterface request = new InputAclSetInterface();
193 request.swIfIndex = swIfIndex;
194 request.l2TableIndex = NOT_DEFINED;
195 request.ip4TableIndex = NOT_DEFINED;
196 request.ip6TableIndex = NOT_DEFINED;
198 if (InterfaceMode.L2.equals(mode)) {
199 final List<Ace> aces = getACEs(acls, writeContext, ace -> true);
200 request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags);
202 final List<Ace> ip4Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp4Path));
203 request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags);
204 final List<Ace> ip6Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp6Path));
205 request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags);
208 final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
209 jvpp.inputAclSetInterface(request);
210 getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
213 private static List<Ace> getACEs(@Nonnull final List<Acl> acls, @Nonnull final WriteContext writeContext,
214 final Predicate<? super Ace> filter) {
215 return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter)
216 .collect(Collectors.toList());
219 private int writeAces(final InstanceIdentifier<?> id, final List<Ace> aces,
220 final AccessLists.DefaultAction defaultAction, final InterfaceMode mode,
221 final int vlanTags) throws WriteFailedException {
222 if (aces.isEmpty()) {
226 int nextTableIndex = configureDefaultAction(id, defaultAction);
227 final ListIterator<Ace> iterator = aces.listIterator(aces.size());
228 while (iterator.hasPrevious()) {
229 final Ace ace = iterator.previous();
230 LOG.trace("Processing ACE: {}", ace);
232 final AceWriter aceWriter =
233 aceWriters.get(AclType.fromAce(ace));
234 if (aceWriter == null) {
235 LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass());
237 final AceType aceType = ace.getMatches().getAceType();
238 final PacketHandling action = ace.getActions().getPacketHandling();
239 final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags);
240 nextTableIndex = createClassifyTable(id, ctRequest);
241 final List<ClassifyAddDelSession> sessionRequests =
242 aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags);
243 for (ClassifyAddDelSession csRequest : sessionRequests) {
244 createClassifySession(id, csRequest);
248 return nextTableIndex;
251 private int configureDefaultAction(@Nonnull final InstanceIdentifier<?> id,
252 final AccessLists.DefaultAction defaultAction)
253 throws WriteFailedException {
254 ClassifyAddDelTable ctRequest = createTable(-1);
255 if (AccessLists.DefaultAction.Permit.equals(defaultAction)) {
256 ctRequest.missNextIndex = -1;
258 ctRequest.missNextIndex = 0;
260 ctRequest.mask = new byte[16];
261 ctRequest.skipNVectors = 0;
262 ctRequest.matchNVectors = 1;
263 return createClassifyTable(id, ctRequest);
266 private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id,
267 @Nonnull final ClassifyAddDelTable request)
268 throws WriteFailedException {
269 final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
271 final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id);
272 return reply.newTableIndex;
275 private void createClassifySession(@Nonnull final InstanceIdentifier<?> id,
276 @Nonnull final ClassifyAddDelSession request)
277 throws WriteFailedException {
278 final CompletionStage<ClassifyAddDelSessionReply> cs = jvpp.classifyAddDelSession(request);
280 getReplyForWrite(cs.toCompletableFuture(), id);
283 private enum AclType {
284 ETH, IP4, IP6, ETH_AND_IP;
287 private static AclType fromAce(final Ace ace) {
288 AclType result = null;
289 final AceType aceType;
291 aceType = ace.getMatches().getAceType();
292 if (aceType instanceof AceEth) {
294 } else if (aceType instanceof AceIp) {
295 final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion();
296 if (aceIpVersion == null) {
297 throw new IllegalArgumentException("Incomplete ACE (ip-version was not provided): " + ace);
299 if (aceIpVersion instanceof AceIpv4) {
301 } else if (aceIpVersion instanceof AceIpv6) {
304 } else if (aceType instanceof AceIpAndEth) {
307 } catch (NullPointerException e) {
308 throw new IllegalArgumentException("Incomplete ACE: " + ace, e);
310 if (result == null) {
311 throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType));