91c7794d38847b346bee7d9c4517c68aa0242942
[hc2vpp.git] /
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter;
24 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
25 import io.fd.honeycomb.translate.vpp.util.WriteTimeoutException;
26 import io.fd.honeycomb.translate.write.WriteContext;
27 import io.fd.honeycomb.translate.write.WriteFailedException;
28 import io.fd.vpp.jvpp.VppBaseCallException;
29 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
30 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
31 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
32 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
33 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
34 import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
35 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.concurrent.CompletionStage;
40 import java.util.stream.Collectors;
41 import java.util.stream.Stream;
42 import javax.annotation.Nonnegative;
43 import javax.annotation.Nonnull;
44 import javax.annotation.Nullable;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
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.matches.AceType;
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.matches.ace.type.AceEth;
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.ace.type.AceIp;
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.ace.ip.AceIpVersion;
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.ace.ip.ace.ip.version.AceIpv4;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceMode;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.access.lists.Acl;
56 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 public final class IetfAClWriter implements JvppReplyConsumer {
61
62     private static final Logger LOG = LoggerFactory.getLogger(IetfAClWriter.class);
63     private final FutureJVppCore jvpp;
64
65     private Map<AclType, AceWriter> aceWriters = new HashMap<>();
66
67     public IetfAClWriter(@Nonnull final FutureJVppCore futureJVppCore) {
68         this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null");
69         aceWriters.put(AclType.ETH, new AceEthWriter(futureJVppCore));
70         aceWriters.put(AclType.IP4, new AceIp4Writer(futureJVppCore));
71         aceWriters.put(AclType.IP6, new AceIp6Writer(futureJVppCore));
72     }
73
74     private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl,
75                                               @Nonnull final WriteContext writeContext) {
76         final String aclName = assignedAcl.getName();
77         final Class<? extends AclBase> aclType = assignedAcl.getType();
78
79         // ietf-acl updates are handled first, so we use writeContext.readAfter
80         final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl>
81                 aclOptional = writeContext.readAfter(IetfAclWriter.ACL_ID.child(
82                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class,
83                 new AclKey(aclName, aclType)));
84         checkArgument(aclOptional.isPresent(), "Acl lists not configured");
85         final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl
86                 acl = aclOptional.get();
87
88         final AccessListEntries accessListEntries = acl.getAccessListEntries();
89         checkArgument(accessListEntries != null, "access list entries not configured");
90
91         return accessListEntries.getAce().stream();
92     }
93
94     void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex)
95             throws WriteTimeoutException, WriteFailedException.DeleteFailedException {
96         final ClassifyTableByInterface request = new ClassifyTableByInterface();
97         request.swIfIndex = swIfIndex;
98
99         try {
100             final CompletionStage<ClassifyTableByInterfaceReply> cs = jvpp.classifyTableByInterface(request);
101             final ClassifyTableByInterfaceReply reply = getReplyForWrite(cs.toCompletableFuture(), id);
102
103             // We unassign and remove all ACL-related classify tables for given interface (we assume we are the only
104             // classify table manager)
105
106             unassignClassifyTables(id, reply);
107
108             removeClassifyTable(id, reply.l2TableId);
109             removeClassifyTable(id, reply.ip4TableId);
110             removeClassifyTable(id, reply.ip6TableId);
111         } catch (VppBaseCallException e) {
112             throw new WriteFailedException.DeleteFailedException(id, e);
113         }
114     }
115
116     private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id,
117                                         final ClassifyTableByInterfaceReply currentState)
118             throws VppBaseCallException, WriteTimeoutException {
119         final InputAclSetInterface request = new InputAclSetInterface();
120         request.isAdd = 0;
121         request.swIfIndex = currentState.swIfIndex;
122         request.l2TableIndex = currentState.l2TableId;
123         request.ip4TableIndex = currentState.ip4TableId;
124         request.ip6TableIndex = currentState.ip6TableId;
125         final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
126                 jvpp.inputAclSetInterface(request);
127         getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
128     }
129
130     private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex)
131             throws VppBaseCallException, WriteTimeoutException {
132
133         if (tableIndex == -1) {
134             return; // classify table id is absent
135         }
136         final ClassifyAddDelTable request = new ClassifyAddDelTable();
137         request.tableIndex = tableIndex;
138         final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
139         getReplyForWrite(cs.toCompletableFuture(), id);
140     }
141
142     void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls,
143                @Nullable final InterfaceMode mode, @Nonnull final WriteContext writeContext)
144             throws VppBaseCallException, WriteTimeoutException {
145         write(id, swIfIndex, mode, acls, writeContext, 0);
146     }
147
148     void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, final InterfaceMode mode,
149                @Nonnull final List<Acl> acls,
150                @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags)
151             throws VppBaseCallException, WriteTimeoutException {
152
153         // filter ACE entries and group by AceType
154         final Map<AclType, List<Ace>> acesByType = acls.stream()
155                 .flatMap(acl -> aclToAceStream(acl, writeContext))
156                 .collect(Collectors.groupingBy(AclType::fromAce));
157
158         final InputAclSetInterface request = new InputAclSetInterface();
159         request.isAdd = 1;
160         request.swIfIndex = swIfIndex;
161         request.l2TableIndex = -1;
162         request.ip4TableIndex = -1;
163         request.ip6TableIndex = -1;
164
165         // for each AceType:
166         for (Map.Entry<AclType, List<Ace>> entry : acesByType.entrySet()) {
167             final AclType aceType = entry.getKey();
168             final List<Ace> aces = entry.getValue();
169             LOG.trace("Processing ACEs of {} type: {}", aceType, aces);
170
171             final AceWriter aceWriter = aceWriters.get(aceType);
172             if (aceWriter == null) {
173                 LOG.warn("AceProcessor for {} not registered. Skipping ACE.", aceType);
174             } else {
175                 aceWriter.write(id, aces, mode, request, numberOfTags);
176             }
177         }
178
179         final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
180                 jvpp.inputAclSetInterface(request);
181         getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
182     }
183
184     private enum AclType {
185         ETH, IP4, IP6;
186
187         @Nonnull
188         private static AclType fromAce(final Ace ace) {
189             AclType result = null;
190             final AceType aceType;
191             try {
192                 aceType = ace.getMatches().getAceType();
193                 if (aceType instanceof AceEth) {
194                     result = ETH;
195                 } else if (aceType instanceof AceIp) {
196                     final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion();
197                     if (aceIpVersion instanceof AceIpv4) {
198                         result = IP4;
199                     } else {
200                         result = IP6;
201                     }
202                 }
203             } catch (NullPointerException e) {
204                 throw new IllegalArgumentException("Incomplete ACE: " + ace, e);
205             }
206             if (result == null) {
207                 throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType));
208             }
209             return result;
210         }
211     }
212 }