Commit 4ce818074b34f955e0aba0b36fd7d9f61d2d4ca2

Authored by Andrew Shvayka
Committed by GitHub
2 parents 0045f006 a1e4bbbb

Merge pull request #4455 from ViacheslavKlimov/feature/snmp-improvement

Improvements for SNMP: SNMP v3 security, RPC, refactoring
Showing 25 changed files with 1439 additions and 456 deletions
@@ -17,18 +17,39 @@ package org.thingsboard.server.common.data.device.data; @@ -17,18 +17,39 @@ package org.thingsboard.server.common.data.device.data;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import lombok.Data; 19 import lombok.Data;
  20 +import lombok.ToString;
  21 +import org.apache.commons.lang3.ObjectUtils;
20 import org.apache.commons.lang3.StringUtils; 22 import org.apache.commons.lang3.StringUtils;
21 import org.thingsboard.server.common.data.DeviceTransportType; 23 import org.thingsboard.server.common.data.DeviceTransportType;
  24 +import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
  25 +import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
22 import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; 26 import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
23 27
  28 +import java.util.Objects;
  29 +
24 @Data 30 @Data
  31 +@ToString(of = {"host", "port", "protocolVersion"})
25 public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration { 32 public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
26 - private String address;  
27 - private int port; 33 + private String host;
  34 + private Integer port;
28 private SnmpProtocolVersion protocolVersion; 35 private SnmpProtocolVersion protocolVersion;
  36 +
  37 + /*
  38 + * For SNMP v1 and v2c
  39 + * */
  40 + private String community;
  41 +
  42 + /*
  43 + * For SNMP v3
  44 + * */
  45 + private String username;
29 private String securityName; 46 private String securityName;
30 - private String authenticationPassphrase; // for SNMP v3  
31 - private String privacyPassphrase; // for SNMP v3 47 + private String contextName;
  48 + private AuthenticationProtocol authenticationProtocol;
  49 + private String authenticationPassphrase;
  50 + private PrivacyProtocol privacyProtocol;
  51 + private String privacyPassphrase;
  52 + private String engineId;
32 53
33 @Override 54 @Override
34 public DeviceTransportType getType() { 55 public DeviceTransportType getType() {
@@ -44,7 +65,21 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur @@ -44,7 +65,21 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
44 65
45 @JsonIgnore 66 @JsonIgnore
46 private boolean isValid() { 67 private boolean isValid() {
47 - return StringUtils.isNotBlank(address) && port > 0 &&  
48 - StringUtils.isNotBlank(securityName) && protocolVersion != null; 68 + boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null;
  69 + if (isValid) {
  70 + switch (protocolVersion) {
  71 + case V1:
  72 + case V2C:
  73 + isValid = StringUtils.isNotEmpty(community);
  74 + break;
  75 + case V3:
  76 + isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
  77 + && contextName != null && authenticationProtocol != null
  78 + && StringUtils.isNotBlank(authenticationPassphrase)
  79 + && privacyProtocol != null && privacyPassphrase != null && engineId != null;
  80 + break;
  81 + }
  82 + }
  83 + return isValid;
49 } 84 }
50 } 85 }
@@ -18,14 +18,15 @@ package org.thingsboard.server.common.data.device.profile; @@ -18,14 +18,15 @@ package org.thingsboard.server.common.data.device.profile;
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import lombok.Data; 19 import lombok.Data;
20 import org.thingsboard.server.common.data.DeviceTransportType; 20 import org.thingsboard.server.common.data.DeviceTransportType;
21 -import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig; 21 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  22 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
22 23
23 import java.util.List; 24 import java.util.List;
24 25
25 @Data 26 @Data
26 public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { 27 public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
27 - private int timeoutMs;  
28 - private int retries; 28 + private Integer timeoutMs;
  29 + private Integer retries;
29 private List<SnmpCommunicationConfig> communicationConfigs; 30 private List<SnmpCommunicationConfig> communicationConfigs;
30 31
31 @Override 32 @Override
@@ -36,12 +37,16 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra @@ -36,12 +37,16 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra
36 @Override 37 @Override
37 public void validate() { 38 public void validate() {
38 if (!isValid()) { 39 if (!isValid()) {
39 - throw new IllegalArgumentException("Transport configuration is not valid"); 40 + throw new IllegalArgumentException("SNMP transport configuration is not valid");
40 } 41 }
41 } 42 }
42 43
43 @JsonIgnore 44 @JsonIgnore
44 private boolean isValid() { 45 private boolean isValid() {
45 - return true; 46 + return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
  47 + && communicationConfigs != null
  48 + && communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
  49 + && communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid)
  50 + .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum();
46 } 51 }
47 } 52 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  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 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import java.util.Arrays;
  19 +import java.util.Optional;
  20 +
  21 +public enum AuthenticationProtocol {
  22 + SHA_1("1.3.6.1.6.3.10.1.1.3"),
  23 + SHA_224("1.3.6.1.6.3.10.1.1.4"),
  24 + SHA_256("1.3.6.1.6.3.10.1.1.5"),
  25 + SHA_384("1.3.6.1.6.3.10.1.1.6"),
  26 + SHA_512("1.3.6.1.6.3.10.1.1.7"),
  27 + MD5("1.3.6.1.6.3.10.1.1.2");
  28 +
  29 + // oids taken from org.snmp4j.security.SecurityProtocol implementations
  30 + private final String oid;
  31 +
  32 + AuthenticationProtocol(String oid) {
  33 + this.oid = oid;
  34 + }
  35 +
  36 + public String getOid() {
  37 + return oid;
  38 + }
  39 +
  40 + public static Optional<AuthenticationProtocol> forName(String name) {
  41 + return Arrays.stream(values())
  42 + .filter(protocol -> protocol.name().equalsIgnoreCase(name))
  43 + .findFirst();
  44 + }
  45 +}
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
@@ -13,13 +13,31 @@ @@ -13,13 +13,31 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp;
17 17
18 -import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 18 +import java.util.Arrays;
  19 +import java.util.Optional;
19 20
20 -public class ClientAttributesTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {  
21 - @Override  
22 - public SnmpCommunicationSpec getSpec() {  
23 - return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING; 21 +public enum PrivacyProtocol {
  22 + DES("1.3.6.1.6.3.10.1.2.2"),
  23 + AES_128("1.3.6.1.6.3.10.1.2.4"),
  24 + AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"),
  25 + AES_256("1.3.6.1.4.1.4976.2.2.1.1.2");
  26 +
  27 + // oids taken from org.snmp4j.security.SecurityProtocol implementations
  28 + private final String oid;
  29 +
  30 + PrivacyProtocol(String oid) {
  31 + this.oid = oid;
  32 + }
  33 +
  34 + public String getOid() {
  35 + return oid;
  36 + }
  37 +
  38 + public static Optional<PrivacyProtocol> forName(String name) {
  39 + return Arrays.stream(values())
  40 + .filter(protocol -> protocol.name().equalsIgnoreCase(name))
  41 + .findFirst();
24 } 42 }
25 } 43 }
@@ -16,25 +16,10 @@ @@ -16,25 +16,10 @@
16 package org.thingsboard.server.common.data.transport.snmp; 16 package org.thingsboard.server.common.data.transport.snmp;
17 17
18 public enum SnmpCommunicationSpec { 18 public enum SnmpCommunicationSpec {
19 - TELEMETRY_QUERYING(true),  
20 - CLIENT_ATTRIBUTES_QUERYING(true), 19 + TELEMETRY_QUERYING,
21 20
  21 + CLIENT_ATTRIBUTES_QUERYING,
22 SHARED_ATTRIBUTES_SETTING, 22 SHARED_ATTRIBUTES_SETTING,
23 23
24 - TELEMETRY_TRAPS_RECEIVING,  
25 - CLIENT_ATTRIBUTES_TRAPS_RECEIVING;  
26 -  
27 - private final boolean isRepeatingQuerying;  
28 -  
29 - SnmpCommunicationSpec() {  
30 - this.isRepeatingQuerying = false;  
31 - }  
32 -  
33 - SnmpCommunicationSpec(boolean isRepeatingQuerying) {  
34 - this.isRepeatingQuerying = isRepeatingQuerying;  
35 - }  
36 -  
37 - public boolean isRepeatingQuerying() {  
38 - return isRepeatingQuerying;  
39 - } 24 + TO_DEVICE_RPC_REQUEST,
40 } 25 }
@@ -16,13 +16,17 @@ @@ -16,13 +16,17 @@
16 package org.thingsboard.server.common.data.transport.snmp; 16 package org.thingsboard.server.common.data.transport.snmp;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.AllArgsConstructor;
19 import lombok.Data; 20 import lombok.Data;
  21 +import lombok.NoArgsConstructor;
20 import org.apache.commons.lang3.StringUtils; 22 import org.apache.commons.lang3.StringUtils;
21 import org.thingsboard.server.common.data.kv.DataType; 23 import org.thingsboard.server.common.data.kv.DataType;
22 24
23 import java.util.regex.Pattern; 25 import java.util.regex.Pattern;
24 26
25 @Data 27 @Data
  28 +@AllArgsConstructor
  29 +@NoArgsConstructor
26 public class SnmpMapping { 30 public class SnmpMapping {
27 private String oid; 31 private String oid;
28 private String key; 32 private String key;
@@ -32,7 +36,6 @@ public class SnmpMapping { @@ -32,7 +36,6 @@ public class SnmpMapping {
32 36
33 @JsonIgnore 37 @JsonIgnore
34 public boolean isValid() { 38 public boolean isValid() {
35 - return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() &&  
36 - StringUtils.isNotBlank(key) && dataType != null; 39 + return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key);
37 } 40 }
38 } 41 }
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java
@@ -13,18 +13,24 @@ @@ -13,18 +13,24 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp.config;
17 17
18 -import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  20 +
  21 +import java.util.List;
  22 +
  23 +@Data
  24 +public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig {
  25 + protected List<SnmpMapping> mappings;
19 26
20 -public class TelemetryTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {  
21 @Override 27 @Override
22 - public SnmpCommunicationSpec getSpec() {  
23 - return SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING; 28 + public boolean isValid() {
  29 + return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
24 } 30 }
25 31
26 @Override 32 @Override
27 - public boolean isValid() {  
28 - return false; 33 + public List<SnmpMapping> getAllMappings() {
  34 + return mappings;
29 } 35 }
30 } 36 }
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp.config;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.EqualsAndHashCode; 19 import lombok.EqualsAndHashCode;
@@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
21 21
22 @EqualsAndHashCode(callSuper = true) 22 @EqualsAndHashCode(callSuper = true)
23 @Data 23 @Data
24 -public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommunicationConfig { 24 +public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
25 private Long queryingFrequencyMs; 25 private Long queryingFrequencyMs;
26 26
27 @Override 27 @Override
@@ -31,6 +31,6 @@ public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommu @@ -31,6 +31,6 @@ public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommu
31 31
32 @Override 32 @Override
33 public boolean isValid() { 33 public boolean isValid() {
34 - return true; 34 + return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid();
35 } 35 }
36 } 36 }
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp.config;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 19 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -23,6 +23,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -23,6 +23,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
23 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 23 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
24 import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; 24 import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
25 import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; 25 import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  26 +import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig;
  27 +import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig;
  28 +import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
26 29
27 import java.util.List; 30 import java.util.List;
28 31
@@ -31,32 +34,21 @@ import java.util.List; @@ -31,32 +34,21 @@ import java.util.List;
31 @JsonSubTypes({ 34 @JsonSubTypes({
32 @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"), 35 @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
33 @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"), 36 @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
34 - @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"),  
35 - @Type(value = TelemetryTrapsReceivingSnmpCommunicationConfig.class, name = "TELEMETRY_TRAPS_RECEIVING"),  
36 - @Type(value = ClientAttributesTrapsReceivingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_TRAPS_RECEIVING") 37 + @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING")
37 }) 38 })
38 -public abstract class SnmpCommunicationConfig {  
39 - protected List<SnmpMapping> mappings; 39 +public interface SnmpCommunicationConfig {
40 40
41 - public List<SnmpMapping> getMappings() {  
42 - return mappings;  
43 - }  
44 -  
45 - public abstract SnmpCommunicationSpec getSpec(); 41 + SnmpCommunicationSpec getSpec();
46 42
47 @JsonIgnore 43 @JsonIgnore
48 - public SnmpMethod getMethod() { 44 + default SnmpMethod getMethod() {
49 return null; 45 return null;
50 } 46 }
51 47
52 @JsonIgnore 48 @JsonIgnore
53 - public boolean isValid() {  
54 - return true;  
55 - } 49 + List<SnmpMapping> getAllMappings();
  50 +
  51 + @JsonIgnore
  52 + boolean isValid();
56 53
57 - public void validate() {  
58 - if (!isValid()) {  
59 - throw new IllegalArgumentException("Communication config is not valid");  
60 - }  
61 - }  
62 } 54 }
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java
@@ -13,13 +13,16 @@ @@ -13,13 +13,16 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
17 17
18 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 18 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  19 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
19 20
20 public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig { 21 public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
  22 +
21 @Override 23 @Override
22 public SnmpCommunicationSpec getSpec() { 24 public SnmpCommunicationSpec getSpec() {
23 return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING; 25 return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
24 } 26 }
  27 +
25 } 28 }
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java
@@ -13,12 +13,14 @@ @@ -13,12 +13,14 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
17 17
18 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 18 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
19 import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; 19 import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  20 +import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
  21 +
  22 +public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
20 23
21 -public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunicationConfig {  
22 @Override 24 @Override
23 public SnmpCommunicationSpec getSpec() { 25 public SnmpCommunicationSpec getSpec() {
24 return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING; 26 return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
@@ -29,8 +31,4 @@ public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunic @@ -29,8 +31,4 @@ public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunic
29 return SnmpMethod.SET; 31 return SnmpMethod.SET;
30 } 32 }
31 33
32 - @Override  
33 - public boolean isValid() {  
34 - return true;  
35 - }  
36 } 34 }
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java
@@ -13,17 +13,20 @@ @@ -13,17 +13,20 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.common.data.transport.snmp.configs; 16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.EqualsAndHashCode; 19 import lombok.EqualsAndHashCode;
20 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 20 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  21 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
21 22
22 @EqualsAndHashCode(callSuper = true) 23 @EqualsAndHashCode(callSuper = true)
23 @Data 24 @Data
24 public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig { 25 public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
  26 +
25 @Override 27 @Override
26 public SnmpCommunicationSpec getSpec() { 28 public SnmpCommunicationSpec getSpec() {
27 return SnmpCommunicationSpec.TELEMETRY_QUERYING; 29 return SnmpCommunicationSpec.TELEMETRY_QUERYING;
28 } 30 }
  31 +
29 } 32 }
@@ -15,9 +15,9 @@ @@ -15,9 +15,9 @@
15 */ 15 */
16 package org.thingsboard.server.transport.snmp; 16 package org.thingsboard.server.transport.snmp;
17 17
  18 +import lombok.Getter;
18 import lombok.RequiredArgsConstructor; 19 import lombok.RequiredArgsConstructor;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 -import org.springframework.beans.factory.annotation.Value;  
21 import org.springframework.context.event.EventListener; 21 import org.springframework.context.event.EventListener;
22 import org.springframework.stereotype.Component; 22 import org.springframework.stereotype.Component;
23 import org.thingsboard.server.common.data.Device; 23 import org.thingsboard.server.common.data.Device;
@@ -42,6 +42,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; @@ -42,6 +42,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
42 import org.thingsboard.server.queue.util.AfterStartUp; 42 import org.thingsboard.server.queue.util.AfterStartUp;
43 import org.thingsboard.server.queue.util.TbSnmpTransportComponent; 43 import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
44 import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService; 44 import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
  45 +import org.thingsboard.server.transport.snmp.service.SnmpAuthService;
45 import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService; 46 import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
46 import org.thingsboard.server.transport.snmp.service.SnmpTransportService; 47 import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
47 import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; 48 import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
@@ -61,18 +62,18 @@ import java.util.stream.Collectors; @@ -61,18 +62,18 @@ import java.util.stream.Collectors;
61 @Slf4j 62 @Slf4j
62 @RequiredArgsConstructor 63 @RequiredArgsConstructor
63 public class SnmpTransportContext extends TransportContext { 64 public class SnmpTransportContext extends TransportContext {
  65 + @Getter
64 private final SnmpTransportService snmpTransportService; 66 private final SnmpTransportService snmpTransportService;
65 private final TransportDeviceProfileCache deviceProfileCache; 67 private final TransportDeviceProfileCache deviceProfileCache;
66 private final TransportService transportService; 68 private final TransportService transportService;
67 private final ProtoTransportEntityService protoEntityService; 69 private final ProtoTransportEntityService protoEntityService;
68 private final SnmpTransportBalancingService balancingService; 70 private final SnmpTransportBalancingService balancingService;
  71 + @Getter
  72 + private final SnmpAuthService snmpAuthService;
69 73
70 private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>(); 74 private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>();
71 private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>(); 75 private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
72 76
73 - @Value("${transport.snmp.underlying_protocol}")  
74 - private String snmpUnderlyingProtocol;  
75 -  
76 @AfterStartUp(order = 2) 77 @AfterStartUp(order = 2)
77 public void initDevicesSessions() { 78 public void initDevicesSessions() {
78 log.info("Initializing SNMP devices sessions"); 79 log.info("Initializing SNMP devices sessions");
@@ -89,18 +90,12 @@ public class SnmpTransportContext extends TransportContext { @@ -89,18 +90,12 @@ public class SnmpTransportContext extends TransportContext {
89 managedDevicesIds.stream() 90 managedDevicesIds.stream()
90 .map(protoEntityService::getDeviceById) 91 .map(protoEntityService::getDeviceById)
91 .collect(Collectors.toList()) 92 .collect(Collectors.toList())
92 - .forEach(device -> {  
93 - try {  
94 - establishDeviceSession(device);  
95 - } catch (Exception e) {  
96 - log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());  
97 - }  
98 - }); 93 + .forEach(this::establishDeviceSession);
99 } 94 }
100 95
101 private void establishDeviceSession(Device device) { 96 private void establishDeviceSession(Device device) {
102 if (device == null) return; 97 if (device == null) return;
103 - log.info("Establishing SNMP device session for device {}", device.getId()); 98 + log.info("Establishing SNMP session for device {}", device.getId());
104 99
105 DeviceProfileId deviceProfileId = device.getDeviceProfileId(); 100 DeviceProfileId deviceProfileId = device.getDeviceProfileId();
106 DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); 101 DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
@@ -114,19 +109,24 @@ public class SnmpTransportContext extends TransportContext { @@ -114,19 +109,24 @@ public class SnmpTransportContext extends TransportContext {
114 SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); 109 SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
115 SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); 110 SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
116 111
117 - DeviceSessionContext deviceSessionContext = new DeviceSessionContext(  
118 - device, deviceProfile, credentials.getCredentialsId(),  
119 - profileTransportConfiguration, deviceTransportConfiguration,  
120 - this, snmpTransportService, snmpUnderlyingProtocol  
121 - );  
122 - registerSessionMsgListener(deviceSessionContext); 112 + DeviceSessionContext deviceSessionContext;
  113 + try {
  114 + deviceSessionContext = new DeviceSessionContext(
  115 + device, deviceProfile, credentials.getCredentialsId(),
  116 + profileTransportConfiguration, deviceTransportConfiguration, this
  117 + );
  118 + registerSessionMsgListener(deviceSessionContext);
  119 + } catch (Exception e) {
  120 + log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString());
  121 + return;
  122 + }
123 sessions.put(device.getId(), deviceSessionContext); 123 sessions.put(device.getId(), deviceSessionContext);
124 snmpTransportService.createQueryingTasks(deviceSessionContext); 124 snmpTransportService.createQueryingTasks(deviceSessionContext);
125 log.info("Established SNMP device session for device {}", device.getId()); 125 log.info("Established SNMP device session for device {}", device.getId());
126 } 126 }
127 127
128 private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) { 128 private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
129 - log.info("Updating SNMP device session for device {}", device.getId()); 129 + log.info("Updating SNMP session for device {}", device.getId());
130 130
131 DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId()); 131 DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
132 if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) { 132 if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
@@ -138,16 +138,21 @@ public class SnmpTransportContext extends TransportContext { @@ -138,16 +138,21 @@ public class SnmpTransportContext extends TransportContext {
138 SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); 138 SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
139 SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); 139 SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
140 140
141 - if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {  
142 - sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);  
143 - sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);  
144 - snmpTransportService.cancelQueryingTasks(sessionContext);  
145 - snmpTransportService.createQueryingTasks(sessionContext);  
146 - } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {  
147 - sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);  
148 - sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);  
149 - } else {  
150 - log.trace("Configuration of the device {} was not updated", device); 141 + try {
  142 + if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
  143 + sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
  144 + sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
  145 + snmpTransportService.cancelQueryingTasks(sessionContext);
  146 + snmpTransportService.createQueryingTasks(sessionContext);
  147 + } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
  148 + sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
  149 + sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
  150 + } else {
  151 + log.trace("Configuration of the device {} was not updated", device);
  152 + }
  153 + } catch (Exception e) {
  154 + log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage());
  155 + destroyDeviceSession(sessionContext);
151 } 156 }
152 } 157 }
153 158
@@ -155,9 +160,10 @@ public class SnmpTransportContext extends TransportContext { @@ -155,9 +160,10 @@ public class SnmpTransportContext extends TransportContext {
155 if (sessionContext == null) return; 160 if (sessionContext == null) return;
156 log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId()); 161 log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
157 sessionContext.close(); 162 sessionContext.close();
  163 + snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
158 transportService.deregisterSession(sessionContext.getSessionInfo()); 164 transportService.deregisterSession(sessionContext.getSessionInfo());
159 - sessions.remove(sessionContext.getDeviceId());  
160 snmpTransportService.cancelQueryingTasks(sessionContext); 165 snmpTransportService.cancelQueryingTasks(sessionContext);
  166 + sessions.remove(sessionContext.getDeviceId());
161 log.trace("Unregistered and removed session"); 167 log.trace("Unregistered and removed session");
162 } 168 }
163 169
@@ -173,7 +179,9 @@ public class SnmpTransportContext extends TransportContext { @@ -173,7 +179,9 @@ public class SnmpTransportContext extends TransportContext {
173 ); 179 );
174 180
175 transportService.registerAsyncSession(sessionInfo, deviceSessionContext); 181 transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
176 - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); 182 + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
  183 + transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
  184 +
177 deviceSessionContext.setSessionInfo(sessionInfo); 185 deviceSessionContext.setSessionInfo(sessionInfo);
178 deviceSessionContext.setDeviceInfo(msg.getDeviceInfo()); 186 deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
179 } else { 187 } else {
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  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 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import com.google.gson.JsonObject;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.snmp4j.PDU;
  21 +import org.snmp4j.ScopedPDU;
  22 +import org.snmp4j.smi.Integer32;
  23 +import org.snmp4j.smi.Null;
  24 +import org.snmp4j.smi.OID;
  25 +import org.snmp4j.smi.OctetString;
  26 +import org.snmp4j.smi.Variable;
  27 +import org.snmp4j.smi.VariableBinding;
  28 +import org.springframework.stereotype.Service;
  29 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  30 +import org.thingsboard.server.common.data.kv.DataType;
  31 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  32 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  33 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  34 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  35 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  36 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  37 +
  38 +import java.util.HashMap;
  39 +import java.util.List;
  40 +import java.util.Map;
  41 +import java.util.Objects;
  42 +import java.util.Optional;
  43 +import java.util.stream.Collectors;
  44 +import java.util.stream.IntStream;
  45 +
  46 +@TbSnmpTransportComponent
  47 +@Service
  48 +@Slf4j
  49 +public class PduService {
  50 + public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
  51 + PDU pdu = setUpPdu(sessionContext);
  52 +
  53 + pdu.setType(communicationConfig.getMethod().getCode());
  54 + pdu.addAll(communicationConfig.getAllMappings().stream()
  55 + .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
  56 + .map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
  57 + .map(value -> {
  58 + Variable variable = toSnmpVariable(value, mapping.getDataType());
  59 + return new VariableBinding(new OID(mapping.getOid()), variable);
  60 + })
  61 + .orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
  62 + .collect(Collectors.toList()));
  63 +
  64 + return pdu;
  65 + }
  66 +
  67 + public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) {
  68 + PDU pdu = setUpPdu(sessionContext);
  69 + pdu.setType(snmpMethod.getCode());
  70 +
  71 + Variable variable = value == null ? Null.instance : toSnmpVariable(value, dataType);
  72 + pdu.add(new VariableBinding(new OID(oid), variable));
  73 +
  74 + return pdu;
  75 + }
  76 +
  77 + private Variable toSnmpVariable(String value, DataType dataType) {
  78 + dataType = dataType == null ? DataType.STRING : dataType;
  79 + Variable variable;
  80 + switch (dataType) {
  81 + case LONG:
  82 + try {
  83 + variable = new Integer32(Integer.parseInt(value));
  84 + break;
  85 + } catch (NumberFormatException ignored) {
  86 + }
  87 + case DOUBLE:
  88 + case BOOLEAN:
  89 + case STRING:
  90 + case JSON:
  91 + default:
  92 + variable = new OctetString(value);
  93 + }
  94 + return variable;
  95 + }
  96 +
  97 + private PDU setUpPdu(DeviceSessionContext sessionContext) {
  98 + PDU pdu;
  99 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
  100 + SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
  101 + switch (snmpVersion) {
  102 + case V1:
  103 + case V2C:
  104 + pdu = new PDU();
  105 + break;
  106 + case V3:
  107 + ScopedPDU scopedPdu = new ScopedPDU();
  108 + scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
  109 + scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
  110 + pdu = scopedPdu;
  111 + break;
  112 + default:
  113 + throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
  114 + }
  115 + return pdu;
  116 + }
  117 +
  118 +
  119 + public JsonObject processPdu(PDU pdu, List<SnmpMapping> responseMappings) {
  120 + Map<OID, String> values = processPdu(pdu);
  121 +
  122 + Map<OID, SnmpMapping> mappings = new HashMap<>();
  123 + if (responseMappings != null) {
  124 + for (SnmpMapping mapping : responseMappings) {
  125 + OID oid = new OID(mapping.getOid());
  126 + mappings.put(oid, mapping);
  127 + }
  128 + }
  129 +
  130 + JsonObject data = new JsonObject();
  131 + values.forEach((oid, value) -> {
  132 + log.trace("Processing variable binding: {} - {}", oid, value);
  133 +
  134 + SnmpMapping mapping = mappings.get(oid);
  135 + if (mapping == null) {
  136 + log.debug("No SNMP mapping for oid {}", oid);
  137 + return;
  138 + }
  139 +
  140 + processValue(mapping.getKey(), mapping.getDataType(), value, data);
  141 + });
  142 +
  143 + return data;
  144 + }
  145 +
  146 + public Map<OID, String> processPdu(PDU pdu) {
  147 + return IntStream.range(0, pdu.size())
  148 + .mapToObj(pdu::get)
  149 + .filter(Objects::nonNull)
  150 + .filter(variableBinding -> !(variableBinding.getVariable() instanceof Null))
  151 + .collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString));
  152 + }
  153 +
  154 + private void processValue(String key, DataType dataType, String value, JsonObject result) {
  155 + switch (dataType) {
  156 + case LONG:
  157 + result.addProperty(key, Long.parseLong(value));
  158 + break;
  159 + case BOOLEAN:
  160 + result.addProperty(key, Boolean.parseBoolean(value));
  161 + break;
  162 + case DOUBLE:
  163 + result.addProperty(key, Double.parseDouble(value));
  164 + break;
  165 + case STRING:
  166 + case JSON:
  167 + default:
  168 + result.addProperty(key, value);
  169 + }
  170 + }
  171 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  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 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.snmp4j.AbstractTarget;
  20 +import org.snmp4j.CommunityTarget;
  21 +import org.snmp4j.Target;
  22 +import org.snmp4j.UserTarget;
  23 +import org.snmp4j.security.SecurityLevel;
  24 +import org.snmp4j.security.SecurityModel;
  25 +import org.snmp4j.security.SecurityProtocols;
  26 +import org.snmp4j.security.USM;
  27 +import org.snmp4j.smi.Address;
  28 +import org.snmp4j.smi.GenericAddress;
  29 +import org.snmp4j.smi.OID;
  30 +import org.snmp4j.smi.OctetString;
  31 +import org.springframework.beans.factory.annotation.Value;
  32 +import org.springframework.stereotype.Service;
  33 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
  35 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  36 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  37 +import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
  38 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  39 +
  40 +import java.util.Optional;
  41 +
  42 +@Service
  43 +@TbSnmpTransportComponent
  44 +@RequiredArgsConstructor
  45 +public class SnmpAuthService {
  46 + private final SnmpTransportService snmpTransportService;
  47 +
  48 + @Value("${transport.snmp.underlying_protocol}")
  49 + private String snmpUnderlyingProtocol;
  50 +
  51 + public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
  52 + AbstractTarget target;
  53 +
  54 + SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
  55 + switch (protocolVersion) {
  56 + case V1:
  57 + CommunityTarget communityTargetV1 = new CommunityTarget();
  58 + communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
  59 + communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
  60 + communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
  61 + target = communityTargetV1;
  62 + break;
  63 + case V2C:
  64 + CommunityTarget communityTargetV2 = new CommunityTarget();
  65 + communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
  66 + communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
  67 + communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
  68 + target = communityTargetV2;
  69 + break;
  70 + case V3:
  71 + OctetString username = new OctetString(deviceTransportConfig.getUsername());
  72 + OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
  73 + OctetString engineId = new OctetString(deviceTransportConfig.getEngineId());
  74 +
  75 + OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid());
  76 + OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid());
  77 + OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
  78 + authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue()));
  79 + OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
  80 + privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue()));
  81 +
  82 + USM usm = snmpTransportService.getSnmp().getUSM();
  83 + if (usm.hasUser(engineId, securityName)) {
  84 + usm.removeAllUsers(username, engineId);
  85 + }
  86 + usm.addLocalizedUser(
  87 + engineId.getValue(), username,
  88 + authenticationProtocol, authenticationPassphrase.getValue(),
  89 + privacyProtocol, privacyPassphrase.getValue()
  90 + );
  91 +
  92 + UserTarget userTarget = new UserTarget();
  93 + userTarget.setSecurityName(securityName);
  94 + userTarget.setAuthoritativeEngineID(engineId.getValue());
  95 + userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
  96 + userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
  97 + target = userTarget;
  98 + break;
  99 + default:
  100 + throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
  101 + }
  102 +
  103 + Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort());
  104 + target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid")));
  105 + target.setTimeout(profileTransportConfig.getTimeoutMs());
  106 + target.setRetries(profileTransportConfig.getRetries());
  107 + target.setVersion(protocolVersion.getCode());
  108 +
  109 + return target;
  110 + }
  111 +
  112 + public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) {
  113 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
  114 + if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) {
  115 + OctetString username = new OctetString(deviceTransportConfiguration.getUsername());
  116 + OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId());
  117 + snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId);
  118 + }
  119 + }
  120 +
  121 +}
@@ -15,48 +15,46 @@ @@ -15,48 +15,46 @@
15 */ 15 */
16 package org.thingsboard.server.transport.snmp.service; 16 package org.thingsboard.server.transport.snmp.service;
17 17
  18 +import com.google.gson.JsonElement;
18 import com.google.gson.JsonObject; 19 import com.google.gson.JsonObject;
  20 +import lombok.Data;
19 import lombok.Getter; 21 import lombok.Getter;
  22 +import lombok.RequiredArgsConstructor;
20 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
21 -import org.apache.commons.lang3.StringUtils;  
22 -import org.snmp4j.CommandResponder;  
23 -import org.snmp4j.CommandResponderEvent;  
24 import org.snmp4j.PDU; 24 import org.snmp4j.PDU;
25 import org.snmp4j.Snmp; 25 import org.snmp4j.Snmp;
26 import org.snmp4j.TransportMapping; 26 import org.snmp4j.TransportMapping;
27 import org.snmp4j.event.ResponseEvent; 27 import org.snmp4j.event.ResponseEvent;
28 -import org.snmp4j.smi.Null;  
29 -import org.snmp4j.smi.OID; 28 +import org.snmp4j.mp.MPv3;
  29 +import org.snmp4j.security.SecurityModels;
  30 +import org.snmp4j.security.SecurityProtocols;
  31 +import org.snmp4j.security.USM;
30 import org.snmp4j.smi.OctetString; 32 import org.snmp4j.smi.OctetString;
31 -import org.snmp4j.smi.TcpAddress;  
32 -import org.snmp4j.smi.UdpAddress;  
33 -import org.snmp4j.smi.VariableBinding;  
34 import org.snmp4j.transport.DefaultTcpTransportMapping; 33 import org.snmp4j.transport.DefaultTcpTransportMapping;
35 import org.snmp4j.transport.DefaultUdpTransportMapping; 34 import org.snmp4j.transport.DefaultUdpTransportMapping;
36 import org.springframework.beans.factory.annotation.Value; 35 import org.springframework.beans.factory.annotation.Value;
37 -import org.springframework.context.annotation.Lazy;  
38 import org.springframework.stereotype.Service; 36 import org.springframework.stereotype.Service;
39 import org.thingsboard.common.util.ThingsBoardThreadFactory; 37 import org.thingsboard.common.util.ThingsBoardThreadFactory;
40 import org.thingsboard.server.common.data.TbTransportService; 38 import org.thingsboard.server.common.data.TbTransportService;
41 -import org.thingsboard.server.common.data.id.DeviceProfileId;  
42 import org.thingsboard.server.common.data.kv.DataType; 39 import org.thingsboard.server.common.data.kv.DataType;
43 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 40 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
44 import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; 41 import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
45 -import org.thingsboard.server.common.data.transport.snmp.configs.RepeatingQueryingSnmpCommunicationConfig;  
46 -import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig; 42 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  43 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  44 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
47 import org.thingsboard.server.common.transport.TransportService; 45 import org.thingsboard.server.common.transport.TransportService;
48 import org.thingsboard.server.common.transport.TransportServiceCallback; 46 import org.thingsboard.server.common.transport.TransportServiceCallback;
49 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 47 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
50 import org.thingsboard.server.gen.transport.TransportProtos; 48 import org.thingsboard.server.gen.transport.TransportProtos;
51 import org.thingsboard.server.queue.util.TbSnmpTransportComponent; 49 import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
52 -import org.thingsboard.server.transport.snmp.SnmpTransportContext;  
53 import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; 50 import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
54 51
55 import javax.annotation.PostConstruct; 52 import javax.annotation.PostConstruct;
56 import javax.annotation.PreDestroy; 53 import javax.annotation.PreDestroy;
57 import java.io.IOException; 54 import java.io.IOException;
  55 +import java.util.Arrays;
  56 +import java.util.Collections;
58 import java.util.EnumMap; 57 import java.util.EnumMap;
59 -import java.util.HashMap;  
60 import java.util.List; 58 import java.util.List;
61 import java.util.Map; 59 import java.util.Map;
62 import java.util.Optional; 60 import java.util.Optional;
@@ -65,44 +63,36 @@ import java.util.concurrent.Executors; @@ -65,44 +63,36 @@ import java.util.concurrent.Executors;
65 import java.util.concurrent.ScheduledExecutorService; 63 import java.util.concurrent.ScheduledExecutorService;
66 import java.util.concurrent.ScheduledFuture; 64 import java.util.concurrent.ScheduledFuture;
67 import java.util.concurrent.TimeUnit; 65 import java.util.concurrent.TimeUnit;
68 -import java.util.function.BiConsumer;  
69 import java.util.stream.Collectors; 66 import java.util.stream.Collectors;
70 -import java.util.stream.Stream;  
71 67
72 @TbSnmpTransportComponent 68 @TbSnmpTransportComponent
73 @Service 69 @Service
74 @Slf4j 70 @Slf4j
75 -public class SnmpTransportService implements TbTransportService, CommandResponder {  
76 - private final SnmpTransportContext snmpTransportContext; 71 +@RequiredArgsConstructor
  72 +public class SnmpTransportService implements TbTransportService {
77 private final TransportService transportService; 73 private final TransportService transportService;
  74 + private final PduService pduService;
78 75
79 @Getter 76 @Getter
80 private Snmp snmp; 77 private Snmp snmp;
81 private ScheduledExecutorService queryingExecutor; 78 private ScheduledExecutorService queryingExecutor;
82 private ExecutorService responseProcessingExecutor; 79 private ExecutorService responseProcessingExecutor;
83 80
84 - private final Map<SnmpCommunicationSpec, BiConsumer<JsonObject, DeviceSessionContext>> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class); 81 + private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class);
  82 + private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
85 83
86 @Value("${transport.snmp.response_processing.parallelism_level}") 84 @Value("${transport.snmp.response_processing.parallelism_level}")
87 private Integer responseProcessingParallelismLevel; 85 private Integer responseProcessingParallelismLevel;
88 @Value("${transport.snmp.underlying_protocol}") 86 @Value("${transport.snmp.underlying_protocol}")
89 private String snmpUnderlyingProtocol; 87 private String snmpUnderlyingProtocol;
90 88
91 - public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext,  
92 - TransportService transportService) {  
93 - this.snmpTransportContext = snmpTransportContext;  
94 - this.transportService = transportService;  
95 - }  
96 -  
97 @PostConstruct 89 @PostConstruct
98 private void init() throws IOException { 90 private void init() throws IOException {
99 - log.info("Initializing SNMP transport service");  
100 -  
101 queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying")); 91 queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
102 responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel); 92 responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
103 93
104 initializeSnmp(); 94 initializeSnmp();
105 - initializeTrapsListener(); 95 + configureResponseDataMappers();
106 configureResponseProcessors(); 96 configureResponseProcessors();
107 97
108 log.info("SNMP transport service initialized"); 98 log.info("SNMP transport service initialized");
@@ -122,53 +112,30 @@ public class SnmpTransportService implements TbTransportService, CommandResponde @@ -122,53 +112,30 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
122 } 112 }
123 snmp = new Snmp(transportMapping); 113 snmp = new Snmp(transportMapping);
124 snmp.listen(); 114 snmp.listen();
125 - }  
126 -  
127 - private void initializeTrapsListener() throws IOException {  
128 - int trapsListeningPort = 1062;  
129 - String bindingAddress = "0.0.0.0/" + trapsListeningPort;  
130 115
131 - TransportMapping<?> transportMapping;  
132 - switch (snmpUnderlyingProtocol) {  
133 - case "udp":  
134 - transportMapping = new DefaultUdpTransportMapping(new UdpAddress(bindingAddress));  
135 - break;  
136 - case "tcp":  
137 - transportMapping = new DefaultTcpTransportMapping(new TcpAddress(bindingAddress));  
138 - break;  
139 - default:  
140 - throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");  
141 - }  
142 -  
143 -  
144 - Snmp trapsSnmp = new Snmp(transportMapping);  
145 - trapsSnmp.addCommandResponder(this);  
146 -  
147 - transportMapping.listen(); 116 + USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
  117 + SecurityModels.getInstance().addSecurityModel(usm);
148 } 118 }
149 119
150 public void createQueryingTasks(DeviceSessionContext sessionContext) { 120 public void createQueryingTasks(DeviceSessionContext sessionContext) {
151 List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() 121 List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
152 - .filter(config -> config.getSpec().isRepeatingQuerying()) 122 + .filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig)
153 .map(config -> { 123 .map(config -> {
154 RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config; 124 RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
155 - return createQueryingTaskForConfig(sessionContext, repeatingCommunicationConfig); 125 + Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs();
  126 +
  127 + return queryingExecutor.scheduleWithFixedDelay(() -> {
  128 + try {
  129 + if (sessionContext.isActive()) {
  130 + sendRequest(sessionContext, repeatingCommunicationConfig);
  131 + }
  132 + } catch (Exception e) {
  133 + log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
  134 + }
  135 + }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
156 }) 136 })
157 .collect(Collectors.toList()); 137 .collect(Collectors.toList());
158 - sessionContext.setQueryingTasks(queryingTasks);  
159 - }  
160 -  
161 - private ScheduledFuture<?> createQueryingTaskForConfig(DeviceSessionContext sessionContext, RepeatingQueryingSnmpCommunicationConfig communicationConfig) {  
162 - Long queryingFrequency = communicationConfig.getQueryingFrequencyMs();  
163 - return queryingExecutor.scheduleWithFixedDelay(() -> {  
164 - try {  
165 - if (sessionContext.isActive()) {  
166 - sendRequest(sessionContext, communicationConfig);  
167 - }  
168 - } catch (Exception e) {  
169 - log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.getMessage());  
170 - }  
171 - }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS); 138 + sessionContext.getQueryingTasks().addAll(queryingTasks);
172 } 139 }
173 140
174 public void cancelQueryingTasks(DeviceSessionContext sessionContext) { 141 public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
@@ -176,187 +143,146 @@ public class SnmpTransportService implements TbTransportService, CommandResponde @@ -176,187 +143,146 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
176 sessionContext.getQueryingTasks().clear(); 143 sessionContext.getQueryingTasks().clear();
177 } 144 }
178 145
179 - public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) throws IOException {  
180 - PDU request = createPdu(communicationConfig);  
181 - executeRequest(sessionContext, request); 146 +
  147 + private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
  148 + sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
182 } 149 }
183 150
184 - public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) throws IOException {  
185 - PDU request = createPduWithValues(communicationConfig, values);  
186 - executeRequest(sessionContext, request); 151 + private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
  152 + PDU request = pduService.createPdu(sessionContext, communicationConfig, values);
  153 + RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings());
  154 + sendRequest(sessionContext, request, requestInfo);
187 } 155 }
188 156
189 - private void executeRequest(DeviceSessionContext sessionContext, PDU request) throws IOException { 157 + private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) {
190 if (request.size() > 0) { 158 if (request.size() > 0) {
191 log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings()); 159 log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
192 - snmp.send(request, sessionContext.getTarget(), sessionContext.getDeviceProfile().getId(), sessionContext); 160 + try {
  161 + snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext);
  162 + } catch (IOException e) {
  163 + log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString());
  164 + }
193 } 165 }
194 } 166 }
195 167
196 - private PDU createPdu(SnmpCommunicationConfig communicationConfig) {  
197 - PDU pdu = new PDU();  
198 - pdu.setType(communicationConfig.getMethod().getCode());  
199 - pdu.addAll(communicationConfig.getMappings().stream()  
200 - .map(mapping -> new VariableBinding(new OID(mapping.getOid())))  
201 - .collect(Collectors.toList()));  
202 - return pdu; 168 + public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) {
  169 + sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
  170 + .filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
  171 + .findFirst()
  172 + .ifPresent(communicationConfig -> {
  173 + Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
  174 + .collect(Collectors.toMap(
  175 + Map.Entry::getKey,
  176 + entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
  177 + ));
  178 + sendRequest(sessionContext, communicationConfig, sharedAttributes);
  179 + });
203 } 180 }
204 181
205 - private PDU createPduWithValues(SnmpCommunicationConfig communicationConfig, Map<String, String> values) {  
206 - PDU pdu = new PDU();  
207 - pdu.setType(communicationConfig.getMethod().getCode());  
208 - pdu.addAll(communicationConfig.getMappings().stream()  
209 - .filter(mapping -> values.containsKey(mapping.getKey()))  
210 - .map(mapping -> {  
211 - String value = values.get(mapping.getKey());  
212 - return new VariableBinding(new OID(mapping.getOid()), new OctetString(value));  
213 - })  
214 - .collect(Collectors.toList()));  
215 - return pdu;  
216 - } 182 + public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) {
  183 + SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName());
  184 + JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject();
217 185
  186 + String oid = Optional.ofNullable(params.get("oid")).map(JsonElement::getAsString).orElse(null);
  187 + String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null);
  188 + DataType dataType = Optional.ofNullable(params.get("dataType")).map(e -> DataType.valueOf(e.getAsString())).orElse(DataType.STRING);
218 189
219 - private void processTrap(CommandResponderEvent event) {  
220 - if (event.getPDU().getType() != PDU.TRAP) return; 190 + if (oid == null || oid.isEmpty()) {
  191 + throw new IllegalArgumentException("OID in to-device RPC request is not specified");
  192 + }
  193 + if (value == null && snmpMethod == SnmpMethod.SET) {
  194 + throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'");
  195 + }
221 196
222 - snmpTransportContext.getSessions().stream()  
223 - .filter(sessionContext -> {  
224 - // TODO: SNMP v3 support  
225 - return sessionContext.getTarget().getSecurityName().equals(OctetString.fromByteArray(event.getSecurityName())) &&  
226 - sessionContext.getTarget().getAddress().equals(event.getPeerAddress());  
227 - })  
228 - .findFirst()  
229 - .ifPresentOrElse(sessionContext -> {  
230 - responseProcessingExecutor.execute(() -> processResponse(sessionContext, event.getPDU()));  
231 - }, () -> {  
232 - log.debug("SNMP event is from unknown source: {}", event);  
233 - }); 197 + PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType);
  198 + sendRequest(sessionContext, request, new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST));
234 } 199 }
235 200
  201 +
236 public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) { 202 public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
237 ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext); 203 ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
238 204
239 if (event.getError() != null) { 205 if (event.getError() != null) {
240 - log.warn("Response error: {}", event.getError().getMessage(), event.getError()); 206 + log.warn("SNMP response error: {}", event.getError().toString());
241 return; 207 return;
242 } 208 }
243 209
244 PDU response = event.getResponse(); 210 PDU response = event.getResponse();
245 if (response == null) { 211 if (response == null) {
246 - log.warn("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID()); 212 + log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
247 return; 213 return;
248 } 214 }
249 - DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();  
250 - log.debug("[{}] Processing SNMP response for device {} with device profile {}: {}",  
251 - response.getRequestID(), sessionContext.getDeviceId(), deviceProfileId, response);  
252 215
253 - responseProcessingExecutor.execute(() -> processResponse(sessionContext, response)); 216 + RequestInfo requestInfo = (RequestInfo) event.getUserObject();
  217 + responseProcessingExecutor.execute(() -> {
  218 + processResponse(sessionContext, response, requestInfo);
  219 + });
254 } 220 }
255 221
256 - private void processResponse(DeviceSessionContext sessionContext, PDU responsePdu) {  
257 - Map<OID, SnmpMapping> mappings = new HashMap<>();  
258 - Map<OID, SnmpCommunicationConfig> configs = new HashMap<>();  
259 - Map<SnmpCommunicationSpec, JsonObject> responses = new EnumMap<>(SnmpCommunicationSpec.class); 222 + private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) {
  223 + ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec());
  224 + if (responseProcessor == null) return;
260 225
261 - for (SnmpCommunicationConfig config : sessionContext.getProfileTransportConfiguration().getCommunicationConfigs()) {  
262 - for (SnmpMapping mapping : config.getMappings()) {  
263 - OID oid = new OID(mapping.getOid());  
264 - mappings.put(oid, mapping);  
265 - configs.put(oid, config);  
266 - }  
267 - responses.put(config.getSpec(), new JsonObject());  
268 - }  
269 -  
270 - for (int i = 0; i < responsePdu.size(); i++) {  
271 - VariableBinding variableBinding = responsePdu.get(i);  
272 - log.trace("Processing variable binding {}: {}", i, variableBinding);  
273 -  
274 - if (variableBinding.getVariable() instanceof Null) {  
275 - log.debug("Response variable is empty");  
276 - continue;  
277 - } 226 + JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo);
278 227
279 - OID oid = variableBinding.getOid();  
280 - if (!mappings.containsKey(oid)) {  
281 - log.debug("No SNMP mapping for oid {}", oid);  
282 - continue;  
283 - }  
284 -  
285 - SnmpCommunicationSpec spec = configs.get(oid).getSpec();  
286 - if (!responseProcessors.containsKey(spec)) {  
287 - log.debug("No response processor found for spec {}", spec);  
288 - continue;  
289 - }  
290 -  
291 - SnmpMapping mapping = mappings.get(oid);  
292 - processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), responses.get(spec));  
293 - }  
294 -  
295 - if (responses.values().stream().allMatch(response -> response.entrySet().isEmpty())) {  
296 - log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), responsePdu.getRequestID()); 228 + if (responseData.entrySet().isEmpty()) {
  229 + log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID());
297 return; 230 return;
298 } 231 }
299 232
300 - responses.forEach((spec, response) -> {  
301 - Optional.ofNullable(responseProcessors.get(spec))  
302 - .ifPresent(responseProcessor -> {  
303 - responseProcessor.accept(response, sessionContext);  
304 - }); 233 + responseProcessor.process(responseData, requestInfo, sessionContext);
  234 + reportActivity(sessionContext.getSessionInfo());
  235 + }
  236 +
  237 + private void configureResponseDataMappers() {
  238 + responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> {
  239 + JsonObject responseData = new JsonObject();
  240 + pduService.processPdu(pdu).forEach((oid, value) -> {
  241 + responseData.addProperty(oid.toDottedString(), value);
  242 + });
  243 + return responseData;
305 }); 244 });
306 245
307 - reportActivity(sessionContext.getSessionInfo()); 246 + ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> {
  247 + return pduService.processPdu(pdu, requestInfo.getResponseMappings());
  248 + };
  249 + Arrays.stream(SnmpCommunicationSpec.values())
  250 + .forEach(communicationSpec -> {
  251 + responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper);
  252 + });
308 } 253 }
309 254
310 private void configureResponseProcessors() { 255 private void configureResponseProcessors() {
311 - Stream.of(SnmpCommunicationSpec.TELEMETRY_QUERYING, SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING)  
312 - .forEach(telemetrySpec -> {  
313 - responseProcessors.put(telemetrySpec, (response, sessionContext) -> {  
314 - TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(response);  
315 - transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);  
316 - log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), response);  
317 - });  
318 - }); 256 + responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> {
  257 + TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData);
  258 + transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null);
  259 + log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
  260 + });
319 261
320 - Stream.of(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING)  
321 - .forEach(clientAttributesSpec -> {  
322 - responseProcessors.put(clientAttributesSpec, (response, sessionContext) -> {  
323 - TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(response);  
324 - transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);  
325 - log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), response);  
326 - });  
327 - }); 262 + responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> {
  263 + TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData);
  264 + transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null);
  265 + log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
  266 + });
  267 +
  268 + responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> {
  269 + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
  270 + .setRequestId(requestInfo.getRequestId())
  271 + .setPayload(JsonConverter.toJson(responseData))
  272 + .build();
  273 + transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null);
  274 + log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId());
  275 + });
328 } 276 }
329 277
330 private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { 278 private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
331 transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() 279 transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
332 - .setAttributeSubscription(false)  
333 - .setRpcSubscription(false) 280 + .setAttributeSubscription(true)
  281 + .setRpcSubscription(true)
334 .setLastActivityTime(System.currentTimeMillis()) 282 .setLastActivityTime(System.currentTimeMillis())
335 .build(), TransportServiceCallback.EMPTY); 283 .build(), TransportServiceCallback.EMPTY);
336 } 284 }
337 285
338 - private void processValue(String key, DataType dataType, String value, JsonObject result) {  
339 - if (StringUtils.isEmpty(value)) return;  
340 -  
341 - switch (dataType) {  
342 - case LONG:  
343 - result.addProperty(key, Long.parseLong(value));  
344 - break;  
345 - case BOOLEAN:  
346 - result.addProperty(key, Boolean.parseBoolean(value));  
347 - break;  
348 - case DOUBLE:  
349 - result.addProperty(key, Double.parseDouble(value));  
350 - break;  
351 - default:  
352 - result.addProperty(key, value);  
353 - }  
354 - }  
355 -  
356 - @Override  
357 - public void processPdu(CommandResponderEvent event) {  
358 - processTrap(event);  
359 - }  
360 286
361 @Override 287 @Override
362 public String getName() { 288 public String getName() {
@@ -381,4 +307,34 @@ public class SnmpTransportService implements TbTransportService, CommandResponde @@ -381,4 +307,34 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
381 } 307 }
382 log.info("SNMP transport stopped!"); 308 log.info("SNMP transport stopped!");
383 } 309 }
  310 +
  311 + @Data
  312 + private static class RequestInfo {
  313 + private Integer requestId;
  314 + private SnmpCommunicationSpec communicationSpec;
  315 + private List<SnmpMapping> responseMappings;
  316 +
  317 + public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec) {
  318 + this.requestId = requestId;
  319 + this.communicationSpec = communicationSpec;
  320 + }
  321 +
  322 + public RequestInfo(SnmpCommunicationSpec communicationSpec) {
  323 + this.communicationSpec = communicationSpec;
  324 + }
  325 +
  326 + public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
  327 + this.communicationSpec = communicationSpec;
  328 + this.responseMappings = responseMappings;
  329 + }
  330 + }
  331 +
  332 + private interface ResponseDataMapper {
  333 + JsonObject map(PDU pdu, RequestInfo requestInfo);
  334 + }
  335 +
  336 + private interface ResponseProcessor {
  337 + void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext);
  338 + }
  339 +
384 } 340 }
@@ -18,31 +18,15 @@ package org.thingsboard.server.transport.snmp.session; @@ -18,31 +18,15 @@ package org.thingsboard.server.transport.snmp.session;
18 import lombok.Getter; 18 import lombok.Getter;
19 import lombok.Setter; 19 import lombok.Setter;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 -import org.snmp4j.AbstractTarget;  
22 -import org.snmp4j.CommunityTarget;  
23 import org.snmp4j.Target; 21 import org.snmp4j.Target;
24 -import org.snmp4j.UserTarget;  
25 import org.snmp4j.event.ResponseEvent; 22 import org.snmp4j.event.ResponseEvent;
26 import org.snmp4j.event.ResponseListener; 23 import org.snmp4j.event.ResponseListener;
27 -import org.snmp4j.security.AuthSHA;  
28 -import org.snmp4j.security.PrivDES;  
29 -import org.snmp4j.security.SecurityLevel;  
30 -import org.snmp4j.security.SecurityModel;  
31 -import org.snmp4j.security.SecurityModels;  
32 -import org.snmp4j.security.USM;  
33 -import org.snmp4j.security.UsmUser;  
34 -import org.snmp4j.smi.GenericAddress;  
35 -import org.snmp4j.smi.OID;  
36 -import org.snmp4j.smi.OctetString;  
37 import org.thingsboard.server.common.data.Device; 24 import org.thingsboard.server.common.data.Device;
38 import org.thingsboard.server.common.data.DeviceProfile; 25 import org.thingsboard.server.common.data.DeviceProfile;
39 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; 26 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
40 import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; 27 import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
41 import org.thingsboard.server.common.data.id.DeviceId; 28 import org.thingsboard.server.common.data.id.DeviceId;
42 -import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;  
43 -import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;  
44 import org.thingsboard.server.common.transport.SessionMsgListener; 29 import org.thingsboard.server.common.transport.SessionMsgListener;
45 -import org.thingsboard.server.common.transport.adaptor.JsonConverter;  
46 import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; 30 import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
47 import org.thingsboard.server.gen.transport.TransportProtos; 31 import org.thingsboard.server.gen.transport.TransportProtos;
48 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 32 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
@@ -51,15 +35,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica @@ -51,15 +35,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica
51 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; 35 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
52 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; 36 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
53 import org.thingsboard.server.transport.snmp.SnmpTransportContext; 37 import org.thingsboard.server.transport.snmp.SnmpTransportContext;
54 -import org.thingsboard.server.transport.snmp.service.SnmpTransportService;  
55 38
56 import java.util.LinkedList; 39 import java.util.LinkedList;
57 import java.util.List; 40 import java.util.List;
58 -import java.util.Map;  
59 import java.util.UUID; 41 import java.util.UUID;
60 import java.util.concurrent.ScheduledFuture; 42 import java.util.concurrent.ScheduledFuture;
61 import java.util.concurrent.atomic.AtomicInteger; 43 import java.util.concurrent.atomic.AtomicInteger;
62 -import java.util.stream.Collectors;  
63 44
64 @Slf4j 45 @Slf4j
65 public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener { 46 public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener {
@@ -76,25 +57,18 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @@ -76,25 +57,18 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
76 private final Device device; 57 private final Device device;
77 58
78 private final SnmpTransportContext snmpTransportContext; 59 private final SnmpTransportContext snmpTransportContext;
79 - private final SnmpTransportService snmpTransportService;  
80 60
81 - @Getter  
82 - @Setter  
83 - private long previousRequestExecutedAt = 0;  
84 private final AtomicInteger msgIdSeq = new AtomicInteger(0); 61 private final AtomicInteger msgIdSeq = new AtomicInteger(0);
85 @Getter 62 @Getter
86 private boolean isActive = true; 63 private boolean isActive = true;
87 - private final String snmpUnderlyingProtocol;  
88 64
89 @Getter 65 @Getter
90 - @Setter  
91 - private List<ScheduledFuture<?>> queryingTasks = new LinkedList<>(); 66 + private final List<ScheduledFuture<?>> queryingTasks = new LinkedList<>();
92 67
93 public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token, 68 public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
94 SnmpDeviceProfileTransportConfiguration profileTransportConfiguration, 69 SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
95 SnmpDeviceTransportConfiguration deviceTransportConfiguration, 70 SnmpDeviceTransportConfiguration deviceTransportConfiguration,
96 - SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService,  
97 - String snmpUnderlyingProtocol) { 71 + SnmpTransportContext snmpTransportContext) throws Exception {
98 super(UUID.randomUUID()); 72 super(UUID.randomUUID());
99 super.setDeviceId(device.getId()); 73 super.setDeviceId(device.getId());
100 super.setDeviceProfile(deviceProfile); 74 super.setDeviceProfile(deviceProfile);
@@ -102,12 +76,10 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @@ -102,12 +76,10 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
102 76
103 this.token = token; 77 this.token = token;
104 this.snmpTransportContext = snmpTransportContext; 78 this.snmpTransportContext = snmpTransportContext;
105 - this.snmpTransportService = snmpTransportService;  
106 79
107 this.profileTransportConfiguration = profileTransportConfiguration; 80 this.profileTransportConfiguration = profileTransportConfiguration;
108 this.deviceTransportConfiguration = deviceTransportConfiguration; 81 this.deviceTransportConfiguration = deviceTransportConfiguration;
109 82
110 - this.snmpUnderlyingProtocol = snmpUnderlyingProtocol;  
111 initializeTarget(profileTransportConfiguration, deviceTransportConfiguration); 83 initializeTarget(profileTransportConfiguration, deviceTransportConfiguration);
112 } 84 }
113 85
@@ -127,62 +99,14 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @@ -127,62 +99,14 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
127 @Override 99 @Override
128 public void onResponse(ResponseEvent event) { 100 public void onResponse(ResponseEvent event) {
129 if (isActive) { 101 if (isActive) {
130 - snmpTransportService.processResponseEvent(this, event); 102 + snmpTransportContext.getSnmpTransportService().processResponseEvent(this, event);
131 } 103 }
132 } 104 }
133 105
134 - public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) { 106 + public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) throws Exception {
135 log.trace("Initializing target for SNMP session of device {}", device); 107 log.trace("Initializing target for SNMP session of device {}", device);
136 -  
137 - AbstractTarget target;  
138 -  
139 - SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();  
140 - switch (protocolVersion) {  
141 - case V1:  
142 - CommunityTarget communityTargetV1 = new CommunityTarget();  
143 - communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);  
144 - communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);  
145 - communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));  
146 - target = communityTargetV1;  
147 - break;  
148 - case V2C:  
149 - CommunityTarget communityTargetV2 = new CommunityTarget();  
150 - communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);  
151 - communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);  
152 - communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));  
153 - target = communityTargetV2;  
154 - break;  
155 - case V3:  
156 - USM usm = new USM();  
157 - SecurityModels.getInstance().addSecurityModel(usm);  
158 -  
159 - OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());  
160 - OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());  
161 - OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());  
162 -  
163 - OID authenticationProtocol = AuthSHA.ID;  
164 - OID privacyProtocol = PrivDES.ID; // FIXME: to config  
165 -  
166 - UsmUser user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);  
167 - snmpTransportService.getSnmp().getUSM().addUser(user);  
168 -  
169 - UserTarget userTarget = new UserTarget();  
170 - userTarget.setSecurityName(securityName);  
171 - userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);  
172 -  
173 - target = userTarget;  
174 - break;  
175 - default:  
176 - throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");  
177 - }  
178 -  
179 - target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));  
180 - target.setTimeout(profileTransportConfig.getTimeoutMs());  
181 - target.setRetries(profileTransportConfig.getRetries());  
182 - target.setVersion(protocolVersion.getCode());  
183 -  
184 - this.target = target;  
185 - log.info("SNMP target initialized: {}", target); 108 + this.target = snmpTransportContext.getSnmpAuthService().setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
  109 + log.debug("SNMP target initialized: {}", target);
186 } 110 }
187 111
188 public void close() { 112 public void close() {
@@ -204,21 +128,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @@ -204,21 +128,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
204 128
205 @Override 129 @Override
206 public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) { 130 public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
207 - profileTransportConfiguration.getCommunicationConfigs().stream()  
208 - .filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)  
209 - .findFirst()  
210 - .ifPresent(communicationConfig -> {  
211 - Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()  
212 - .collect(Collectors.toMap(  
213 - Map.Entry::getKey,  
214 - entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()  
215 - ));  
216 - try {  
217 - snmpTransportService.sendRequest(this, communicationConfig, sharedAttributes);  
218 - } catch (Exception e) {  
219 - log.error("Failed to send request with shared attributes to SNMP device {}: {}", getDeviceId(), e.getMessage());  
220 - }  
221 - }); 131 + snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification);
222 } 132 }
223 133
224 @Override 134 @Override
@@ -227,6 +137,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @@ -227,6 +137,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
227 137
228 @Override 138 @Override
229 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { 139 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
  140 + snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
230 } 141 }
231 142
232 @Override 143 @Override
@@ -73,32 +73,6 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { @@ -73,32 +73,6 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent {
73 } 73 }
74 } 74 }
75 75
76 - public static void main(String[] args) throws IOException {  
77 - SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");  
78 -  
79 - device.start();  
80 - device.setUpMappings(Map.of(  
81 - ".1.3.6.1.2.1.1.1.50", "12",  
82 - ".1.3.6.1.2.1.2.1.52", "56",  
83 - ".1.3.6.1.2.1.3.1.54", "yes",  
84 - ".1.3.6.1.2.1.7.1.58", ""  
85 - ));  
86 -  
87 -  
88 -// while (true) {  
89 -// new Scanner(System.in).nextLine();  
90 -// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));  
91 -// System.out.println("sent");  
92 -// }  
93 -  
94 -// Snmp snmp = new Snmp(device.transportMappings[0]);  
95 -// device.snmp.addCommandResponder(event -> {  
96 -// System.out.println(event);  
97 -// });  
98 -  
99 - new Scanner(System.in).nextLine();  
100 - }  
101 -  
102 76
103 private final Target target; 77 private final Target target;
104 private final Address address; 78 private final Address address;
@@ -15,38 +15,731 @@ @@ -15,38 +15,731 @@
15 */ 15 */
16 package org.thingsboard.server.transport.snmp; 16 package org.thingsboard.server.transport.snmp;
17 17
18 -import org.snmp4j.UserTarget; 18 +import org.snmp4j.MessageDispatcherImpl;
  19 +import org.snmp4j.TransportMapping;
  20 +import org.snmp4j.agent.BaseAgent;
  21 +import org.snmp4j.agent.CommandProcessor;
  22 +import org.snmp4j.agent.DuplicateRegistrationException;
  23 +import org.snmp4j.agent.MOGroup;
  24 +import org.snmp4j.agent.ManagedObject;
  25 +import org.snmp4j.agent.mo.DefaultMOMutableRow2PC;
  26 +import org.snmp4j.agent.mo.DefaultMOTable;
  27 +import org.snmp4j.agent.mo.MOAccessImpl;
  28 +import org.snmp4j.agent.mo.MOColumn;
  29 +import org.snmp4j.agent.mo.MOMutableColumn;
  30 +import org.snmp4j.agent.mo.MOMutableTableModel;
  31 +import org.snmp4j.agent.mo.MOScalar;
  32 +import org.snmp4j.agent.mo.MOTableIndex;
  33 +import org.snmp4j.agent.mo.MOTableRow;
  34 +import org.snmp4j.agent.mo.MOTableSubIndex;
  35 +import org.snmp4j.agent.mo.ext.AgentppSimulationMib;
  36 +import org.snmp4j.agent.mo.snmp.RowStatus;
  37 +import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
  38 +import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
  39 +import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
  40 +import org.snmp4j.agent.mo.snmp.StorageType;
  41 +import org.snmp4j.agent.mo.snmp.TransportDomains;
  42 +import org.snmp4j.agent.mo.snmp.VacmMIB;
  43 +import org.snmp4j.agent.mo.snmp4j.example.Snmp4jHeartbeatMib;
  44 +import org.snmp4j.agent.security.MutableVACM;
  45 +import org.snmp4j.mp.MPv1;
  46 +import org.snmp4j.mp.MPv2c;
  47 +import org.snmp4j.mp.MPv3;
  48 +import org.snmp4j.mp.MessageProcessingModel;
  49 +import org.snmp4j.security.AuthHMAC192SHA256;
  50 +import org.snmp4j.security.AuthMD5;
19 import org.snmp4j.security.AuthSHA; 51 import org.snmp4j.security.AuthSHA;
  52 +import org.snmp4j.security.PrivAES128;
  53 +import org.snmp4j.security.PrivAES192;
  54 +import org.snmp4j.security.PrivAES256;
20 import org.snmp4j.security.PrivDES; 55 import org.snmp4j.security.PrivDES;
21 import org.snmp4j.security.SecurityLevel; 56 import org.snmp4j.security.SecurityLevel;
  57 +import org.snmp4j.security.SecurityModel;
22 import org.snmp4j.security.SecurityModels; 58 import org.snmp4j.security.SecurityModels;
  59 +import org.snmp4j.security.SecurityProtocols;
23 import org.snmp4j.security.USM; 60 import org.snmp4j.security.USM;
24 import org.snmp4j.security.UsmUser; 61 import org.snmp4j.security.UsmUser;
  62 +import org.snmp4j.smi.Address;
  63 +import org.snmp4j.smi.Gauge32;
  64 +import org.snmp4j.smi.GenericAddress;
  65 +import org.snmp4j.smi.Integer32;
25 import org.snmp4j.smi.OID; 66 import org.snmp4j.smi.OID;
26 import org.snmp4j.smi.OctetString; 67 import org.snmp4j.smi.OctetString;
  68 +import org.snmp4j.smi.SMIConstants;
  69 +import org.snmp4j.smi.TcpAddress;
  70 +import org.snmp4j.smi.TimeTicks;
  71 +import org.snmp4j.smi.UdpAddress;
  72 +import org.snmp4j.smi.Variable;
  73 +import org.snmp4j.transport.DefaultTcpTransportMapping;
  74 +import org.snmp4j.transport.TransportMappings;
  75 +import org.snmp4j.util.ThreadPool;
27 76
  77 +import java.io.File;
28 import java.io.IOException; 78 import java.io.IOException;
  79 +import java.util.Map;
29 80
30 -public class SnmpDeviceSimulatorV3 extends SnmpDeviceSimulatorV2 {  
31 - public SnmpDeviceSimulatorV3(int port, String securityName, String authenticationPassphrase, String privacyPassphrase) throws IOException {  
32 - super(12, null);  
33 -// super(new File("conf.agent"), new File("bootCounter.agent")); 81 +/**
  82 + * The TestAgent is a sample SNMP agent implementation of all
  83 + * features (MIB implementations) provided by the SNMP4J-Agent framework.
  84 + *
  85 + * Note, for snmp4s, this code is mostly a copy from snmp4j.
  86 + * And don't remove snmp users
  87 + *
  88 + */
  89 +public class SnmpDeviceSimulatorV3 extends BaseAgent {
  90 + protected String address;
  91 + private Snmp4jHeartbeatMib heartbeatMIB;
  92 + private AgentppSimulationMib agentppSimulationMIB;
  93 +
  94 + public SnmpDeviceSimulatorV3(CommandProcessor processor) throws IOException {
  95 + super(new File("SNMP4JTestAgentBC.cfg"), new File("SNMP4JTestAgentConfig.cfg"),
  96 + processor);
  97 + agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
  98 + }
  99 +
  100 + public void setUpMappings(Map<String, String> oidToResponseMappings) {
  101 + unregisterManagedObject(getSnmpv2MIB());
  102 + oidToResponseMappings.forEach((oid, response) -> {
  103 + registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
  104 + });
  105 + }
  106 + protected void registerManagedObject(ManagedObject mo) {
  107 + try {
  108 + server.register(mo, null);
  109 + } catch (DuplicateRegistrationException ex) {
  110 + throw new RuntimeException(ex);
  111 + }
  112 + }
  113 +
  114 + protected void unregisterManagedObject(MOGroup moGroup) {
  115 + moGroup.unregisterMOs(server, getContext(moGroup));
  116 + }
  117 +
  118 + protected void registerManagedObjects() {
  119 + try {
  120 + server.register(createStaticIfTable(), null);
  121 + server.register(createStaticIfXTable(), null);
  122 + agentppSimulationMIB.registerMOs(server, null);
  123 + heartbeatMIB.registerMOs(server, null);
  124 + } catch (DuplicateRegistrationException ex) {
  125 + ex.printStackTrace();
  126 + }
  127 + }
  128 +
  129 + protected void addNotificationTargets(SnmpTargetMIB targetMIB,
  130 + SnmpNotificationMIB notificationMIB) {
  131 + targetMIB.addDefaultTDomains();
  132 +
  133 + targetMIB.addTargetAddress(new OctetString("notificationV2c"),
  134 + TransportDomains.transportDomainUdpIpv4,
  135 + new OctetString(new UdpAddress("127.0.0.1/162").getValue()),
  136 + 200, 1,
  137 + new OctetString("notify"),
  138 + new OctetString("v2c"),
  139 + StorageType.permanent);
  140 + targetMIB.addTargetAddress(new OctetString("notificationV3"),
  141 + TransportDomains.transportDomainUdpIpv4,
  142 + new OctetString(new UdpAddress("127.0.0.1/1162").getValue()),
  143 + 200, 1,
  144 + new OctetString("notify"),
  145 + new OctetString("v3notify"),
  146 + StorageType.permanent);
  147 + targetMIB.addTargetParams(new OctetString("v2c"),
  148 + MessageProcessingModel.MPv2c,
  149 + SecurityModel.SECURITY_MODEL_SNMPv2c,
  150 + new OctetString("cpublic"),
  151 + SecurityLevel.AUTH_PRIV,
  152 + StorageType.permanent);
  153 + targetMIB.addTargetParams(new OctetString("v3notify"),
  154 + MessageProcessingModel.MPv3,
  155 + SecurityModel.SECURITY_MODEL_USM,
  156 + new OctetString("v3notify"),
  157 + SecurityLevel.NOAUTH_NOPRIV,
  158 + StorageType.permanent);
  159 + notificationMIB.addNotifyEntry(new OctetString("default"),
  160 + new OctetString("notify"),
  161 + SnmpNotificationMIB.SnmpNotifyTypeEnum.inform,
  162 + StorageType.permanent);
  163 + }
  164 + protected void addViews(VacmMIB vacm) {
  165 + vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,
  166 + new OctetString("cpublic"),
  167 + new OctetString("v1v2group"),
  168 + StorageType.nonVolatile);
  169 + vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c,
  170 + new OctetString("cpublic"),
  171 + new OctetString("v1v2group"),
  172 + StorageType.nonVolatile);
  173 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  174 + new OctetString("SHADES"),
  175 + new OctetString("v3group"),
  176 + StorageType.nonVolatile);
  177 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  178 + new OctetString("MD5DES"),
  179 + new OctetString("v3group"),
  180 + StorageType.nonVolatile);
  181 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  182 + new OctetString("TEST"),
  183 + new OctetString("v3test"),
  184 + StorageType.nonVolatile);
  185 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  186 + new OctetString("SHA"),
  187 + new OctetString("v3restricted"),
  188 + StorageType.nonVolatile);
  189 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  190 + new OctetString("SHAAES128"),
  191 + new OctetString("v3group"),
  192 + StorageType.nonVolatile);
  193 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  194 + new OctetString("SHAAES192"),
  195 + new OctetString("v3group"),
  196 + StorageType.nonVolatile);
  197 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  198 + new OctetString("SHAAES256"),
  199 + new OctetString("v3group"),
  200 + StorageType.nonVolatile);
  201 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  202 + new OctetString("MD5AES128"),
  203 + new OctetString("v3group"),
  204 + StorageType.nonVolatile);
  205 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  206 + new OctetString("MD5AES192"),
  207 + new OctetString("v3group"),
  208 + StorageType.nonVolatile);
  209 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  210 + new OctetString("MD5AES256"),
  211 + new OctetString("v3group"),
  212 + StorageType.nonVolatile);
  213 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  214 + new OctetString("aboba"),
  215 + new OctetString("v3group"),
  216 + StorageType.nonVolatile);
  217 + //============================================//
  218 + // agent5-auth-priv
  219 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  220 + new OctetString("agent5"),
  221 + new OctetString("v3group"),
  222 + StorageType.nonVolatile);
  223 + //===========================================//
  224 + // agent002
  225 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  226 + new OctetString("agent002"),
  227 + new OctetString("v3group"),
  228 + StorageType.nonVolatile);
  229 + //===========================================//
  230 + // user001-auth-no-priv
  231 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  232 + new OctetString("user001"),
  233 + new OctetString("group001"),
  234 + StorageType.nonVolatile);
  235 + //===========================================//
  236 +
  237 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  238 + new OctetString("v3notify"),
  239 + new OctetString("v3group"),
  240 + StorageType.nonVolatile);
  241 +
  242 + //===========================================//
  243 + // group auth no priv
  244 + vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
  245 + new OctetString("v3notify-auth"),
  246 + new OctetString("group001"),
  247 + StorageType.nonVolatile);
  248 + //===========================================//
  249 +
  250 +
  251 +
  252 + // my conf
  253 + vacm.addAccess(new OctetString("group001"), new OctetString("public"),
  254 + SecurityModel.SECURITY_MODEL_USM,
  255 + SecurityLevel.AUTH_NOPRIV,
  256 + MutableVACM.VACM_MATCH_EXACT,
  257 + new OctetString("fullReadView"),
  258 + new OctetString("fullWriteView"),
  259 + new OctetString("fullNotifyView"),
  260 + StorageType.nonVolatile);
  261 +
  262 + vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
  263 + SecurityModel.SECURITY_MODEL_ANY,
  264 + SecurityLevel.NOAUTH_NOPRIV,
  265 + MutableVACM.VACM_MATCH_EXACT,
  266 + new OctetString("fullReadView"),
  267 + new OctetString("fullWriteView"),
  268 + new OctetString("fullNotifyView"),
  269 + StorageType.nonVolatile);
  270 + vacm.addAccess(new OctetString("v3group"), new OctetString(),
  271 + SecurityModel.SECURITY_MODEL_USM,
  272 + SecurityLevel.AUTH_PRIV,
  273 + MutableVACM.VACM_MATCH_EXACT,
  274 + new OctetString("fullReadView"),
  275 + new OctetString("fullWriteView"),
  276 + new OctetString("fullNotifyView"),
  277 + StorageType.nonVolatile);
  278 + vacm.addAccess(new OctetString("v3restricted"), new OctetString(),
  279 + SecurityModel.SECURITY_MODEL_USM,
  280 + SecurityLevel.NOAUTH_NOPRIV,
  281 + MutableVACM.VACM_MATCH_EXACT,
  282 + new OctetString("restrictedReadView"),
  283 + new OctetString("restrictedWriteView"),
  284 + new OctetString("restrictedNotifyView"),
  285 + StorageType.nonVolatile);
  286 + vacm.addAccess(new OctetString("v3test"), new OctetString(),
  287 + SecurityModel.SECURITY_MODEL_USM,
  288 + SecurityLevel.AUTH_PRIV,
  289 + MutableVACM.VACM_MATCH_EXACT,
  290 + new OctetString("testReadView"),
  291 + new OctetString("testWriteView"),
  292 + new OctetString("testNotifyView"),
  293 + StorageType.nonVolatile);
  294 +
  295 + vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
  296 + new OctetString(), VacmMIB.vacmViewIncluded,
  297 + StorageType.nonVolatile);
  298 + vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"),
  299 + new OctetString(), VacmMIB.vacmViewIncluded,
  300 + StorageType.nonVolatile);
  301 + vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"),
  302 + new OctetString(), VacmMIB.vacmViewIncluded,
  303 + StorageType.nonVolatile);
  304 +
  305 + vacm.addViewTreeFamily(new OctetString("restrictedReadView"),
  306 + new OID("1.3.6.1.2"),
  307 + new OctetString(), VacmMIB.vacmViewIncluded,
  308 + StorageType.nonVolatile);
  309 + vacm.addViewTreeFamily(new OctetString("restrictedWriteView"),
  310 + new OID("1.3.6.1.2.1"),
  311 + new OctetString(),
  312 + VacmMIB.vacmViewIncluded,
  313 + StorageType.nonVolatile);
  314 + vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
  315 + new OID("1.3.6.1.2"),
  316 + new OctetString(), VacmMIB.vacmViewIncluded,
  317 + StorageType.nonVolatile);
  318 + vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
  319 + new OID("1.3.6.1.6.3.1"),
  320 + new OctetString(), VacmMIB.vacmViewIncluded,
  321 + StorageType.nonVolatile);
34 322
35 - USM usm = new USM();  
36 - SecurityModels.getInstance().addSecurityModel(usm); 323 + vacm.addViewTreeFamily(new OctetString("testReadView"),
  324 + new OID("1.3.6.1.2"),
  325 + new OctetString(), VacmMIB.vacmViewIncluded,
  326 + StorageType.nonVolatile);
  327 + vacm.addViewTreeFamily(new OctetString("testReadView"),
  328 + new OID("1.3.6.1.2.1.1"),
  329 + new OctetString(), VacmMIB.vacmViewExcluded,
  330 + StorageType.nonVolatile);
  331 + vacm.addViewTreeFamily(new OctetString("testWriteView"),
  332 + new OID("1.3.6.1.2.1"),
  333 + new OctetString(),
  334 + VacmMIB.vacmViewIncluded,
  335 + StorageType.nonVolatile);
  336 + vacm.addViewTreeFamily(new OctetString("testNotifyView"),
  337 + new OID("1.3.6.1.2"),
  338 + new OctetString(), VacmMIB.vacmViewIncluded,
  339 + StorageType.nonVolatile);
37 340
  341 + }
  342 +
  343 + protected void addUsmUser(USM usm) {
  344 + UsmUser user = new UsmUser(new OctetString("SHADES"),
  345 + AuthSHA.ID,
  346 + new OctetString("SHADESAuthPassword"),
  347 + PrivDES.ID,
  348 + new OctetString("SHADESPrivPassword"));
  349 +// usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  350 + usm.addUser(user.getSecurityName(), null, user);
  351 + user = new UsmUser(new OctetString("TEST"),
  352 + AuthSHA.ID,
  353 + new OctetString("maplesyrup"),
  354 + PrivDES.ID,
  355 + new OctetString("maplesyrup"));
  356 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  357 + user = new UsmUser(new OctetString("SHA"),
  358 + AuthSHA.ID,
  359 + new OctetString("SHAAuthPassword"),
  360 + null,
  361 + null);
  362 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  363 + user = new UsmUser(new OctetString("SHADES"),
  364 + AuthSHA.ID,
  365 + new OctetString("SHADESAuthPassword"),
  366 + PrivDES.ID,
  367 + new OctetString("SHADESPrivPassword"));
  368 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  369 + user = new UsmUser(new OctetString("MD5DES"),
  370 + AuthMD5.ID,
  371 + new OctetString("MD5DESAuthPassword"),
  372 + PrivDES.ID,
  373 + new OctetString("MD5DESPrivPassword"));
  374 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  375 + user = new UsmUser(new OctetString("SHAAES128"),
  376 + AuthSHA.ID,
  377 + new OctetString("SHAAES128AuthPassword"),
  378 + PrivAES128.ID,
  379 + new OctetString("SHAAES128PrivPassword"));
  380 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  381 + user = new UsmUser(new OctetString("SHAAES192"),
  382 + AuthSHA.ID,
  383 + new OctetString("SHAAES192AuthPassword"),
  384 + PrivAES192.ID,
  385 + new OctetString("SHAAES192PrivPassword"));
  386 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  387 + user = new UsmUser(new OctetString("SHAAES256"),
  388 + AuthSHA.ID,
  389 + new OctetString("SHAAES256AuthPassword"),
  390 + PrivAES256.ID,
  391 + new OctetString("SHAAES256PrivPassword"));
  392 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  393 +
  394 + user = new UsmUser(new OctetString("MD5AES128"),
  395 + AuthMD5.ID,
  396 + new OctetString("MD5AES128AuthPassword"),
  397 + PrivAES128.ID,
  398 + new OctetString("MD5AES128PrivPassword"));
  399 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  400 + user = new UsmUser(new OctetString("MD5AES192"),
  401 + AuthHMAC192SHA256.ID,
  402 + new OctetString("MD5AES192AuthPassword"),
  403 + PrivAES192.ID,
  404 + new OctetString("MD5AES192PrivPassword"));
  405 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  406 + //==============================================================
  407 + user = new UsmUser(new OctetString("MD5AES256"),
  408 + AuthMD5.ID,
  409 + new OctetString("MD5AES256AuthPassword"),
  410 + PrivAES256.ID,
  411 + new OctetString("MD5AES256PrivPassword"));
  412 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  413 + user = new UsmUser(new OctetString("MD5AES256"),
  414 + AuthMD5.ID,
  415 + new OctetString("MD5AES256AuthPassword"),
  416 + PrivAES256.ID,
  417 + new OctetString("MD5AES256PrivPassword"));
  418 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  419 +
  420 + OctetString securityName = new OctetString("aboba");
  421 + OctetString authenticationPassphrase = new OctetString("abobaaboba");
  422 + OctetString privacyPassphrase = new OctetString("abobaaboba");
38 OID authenticationProtocol = AuthSHA.ID; 423 OID authenticationProtocol = AuthSHA.ID;
39 - OID privacyProtocol = PrivDES.ID; 424 + OID privacyProtocol = PrivDES.ID; // FIXME: to config
  425 + user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
  426 + usm.addUser(user);
  427 +
  428 + //===============================================================//
  429 + user = new UsmUser(new OctetString("agent5"),
  430 + AuthSHA.ID,
  431 + new OctetString("authpass"),
  432 + PrivDES.ID,
  433 + new OctetString("privpass"));
  434 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  435 + //===============================================================//
  436 + // user001
  437 + user = new UsmUser(new OctetString("user001"),
  438 + AuthSHA.ID,
  439 + new OctetString("authpass"),
  440 + null, null);
  441 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  442 + //===============================================================//
  443 + // user002
  444 + user = new UsmUser(new OctetString("user001"),
  445 + null,
  446 + null,
  447 + null, null);
  448 + usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  449 + //===============================================================//
  450 +
  451 + user = new UsmUser(new OctetString("v3notify"),
  452 + null,
  453 + null,
  454 + null,
  455 + null);
  456 + usm.addUser(user.getSecurityName(), null, user);
  457 +
  458 + this.usm = usm;
  459 + }
  460 +
  461 + private static DefaultMOTable createStaticIfXTable() {
  462 + MOTableSubIndex[] subIndexes =
  463 + new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
  464 + MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
  465 + MOColumn[] columns = new MOColumn[19];
  466 + int c = 0;
  467 + columns[c++] =
  468 + new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
  469 + MOAccessImpl.ACCESS_READ_ONLY); // ifName
  470 + columns[c++] =
  471 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  472 + MOAccessImpl.ACCESS_READ_ONLY); // ifInMulticastPkts
  473 + columns[c++] =
  474 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  475 + MOAccessImpl.ACCESS_READ_ONLY); // ifInBroadcastPkts
  476 + columns[c++] =
  477 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  478 + MOAccessImpl.ACCESS_READ_ONLY); // ifOutMulticastPkts
  479 + columns[c++] =
  480 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  481 + MOAccessImpl.ACCESS_READ_ONLY); // ifOutBroadcastPkts
  482 + columns[c++] =
  483 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  484 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInOctets
  485 + columns[c++] =
  486 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  487 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInUcastPkts
  488 + columns[c++] =
  489 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  490 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInMulticastPkts
  491 + columns[c++] =
  492 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  493 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCInBroadcastPkts
  494 + columns[c++] =
  495 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  496 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutOctets
  497 + columns[c++] =
  498 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  499 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutUcastPkts
  500 + columns[c++] =
  501 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  502 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutMulticastPkts
  503 + columns[c++] =
  504 + new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
  505 + MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutBroadcastPkts
  506 + columns[c++] =
  507 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  508 + MOAccessImpl.ACCESS_READ_WRITE); // ifLinkUpDownTrapEnable
  509 + columns[c++] =
  510 + new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
  511 + MOAccessImpl.ACCESS_READ_ONLY); // ifHighSpeed
  512 + columns[c++] =
  513 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  514 + MOAccessImpl.ACCESS_READ_WRITE); // ifPromiscuousMode
  515 + columns[c++] =
  516 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  517 + MOAccessImpl.ACCESS_READ_ONLY); // ifConnectorPresent
  518 + columns[c++] =
  519 + new MOMutableColumn(c, SMIConstants.SYNTAX_OCTET_STRING, // ifAlias
  520 + MOAccessImpl.ACCESS_READ_WRITE, null);
  521 + columns[c++] =
  522 + new MOColumn(c, SMIConstants.SYNTAX_TIMETICKS,
  523 + MOAccessImpl.ACCESS_READ_ONLY); // ifCounterDiscontinuityTime
  524 +
  525 + DefaultMOTable ifXTable =
  526 + new DefaultMOTable(new OID("1.3.6.1.2.1.31.1.1.1"), indexDef, columns);
  527 + MOMutableTableModel model = (MOMutableTableModel) ifXTable.getModel();
  528 + Variable[] rowValues1 = new Variable[] {
  529 + new OctetString("Ethernet-0"),
  530 + new Integer32(1),
  531 + new Integer32(2),
  532 + new Integer32(3),
  533 + new Integer32(4),
  534 + new Integer32(5),
  535 + new Integer32(6),
  536 + new Integer32(7),
  537 + new Integer32(8),
  538 + new Integer32(9),
  539 + new Integer32(10),
  540 + new Integer32(11),
  541 + new Integer32(12),
  542 + new Integer32(13),
  543 + new Integer32(14),
  544 + new Integer32(15),
  545 + new Integer32(16),
  546 + new OctetString("My eth"),
  547 + new TimeTicks(1000)
  548 + };
  549 + Variable[] rowValues2 = new Variable[] {
  550 + new OctetString("Loopback"),
  551 + new Integer32(21),
  552 + new Integer32(22),
  553 + new Integer32(23),
  554 + new Integer32(24),
  555 + new Integer32(25),
  556 + new Integer32(26),
  557 + new Integer32(27),
  558 + new Integer32(28),
  559 + new Integer32(29),
  560 + new Integer32(30),
  561 + new Integer32(31),
  562 + new Integer32(32),
  563 + new Integer32(33),
  564 + new Integer32(34),
  565 + new Integer32(35),
  566 + new Integer32(36),
  567 + new OctetString("My loop"),
  568 + new TimeTicks(2000)
  569 + };
  570 + model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
  571 + model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
  572 + ifXTable.setVolatile(true);
  573 + return ifXTable;
  574 + }
  575 +
  576 + private static DefaultMOTable createStaticIfTable() {
  577 + MOTableSubIndex[] subIndexes =
  578 + new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
  579 + MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
  580 + MOColumn[] columns = new MOColumn[8];
  581 + int c = 0;
  582 + columns[c++] =
  583 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  584 + MOAccessImpl.ACCESS_READ_ONLY); // ifIndex
  585 + columns[c++] =
  586 + new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
  587 + MOAccessImpl.ACCESS_READ_ONLY); // ifDescr
  588 + columns[c++] =
  589 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  590 + MOAccessImpl.ACCESS_READ_ONLY); // ifType
  591 + columns[c++] =
  592 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  593 + MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
  594 + columns[c++] =
  595 + new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
  596 + MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
  597 + columns[c++] =
  598 + new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
  599 + MOAccessImpl.ACCESS_READ_ONLY); // ifPhysAddress
  600 + columns[c++] =
  601 + new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, // ifAdminStatus
  602 + MOAccessImpl.ACCESS_READ_WRITE, null);
  603 + columns[c++] =
  604 + new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
  605 + MOAccessImpl.ACCESS_READ_ONLY); // ifOperStatus
40 606
41 - UsmUser user = new UsmUser(new OctetString(securityName), authenticationProtocol, new OctetString(authenticationPassphrase), privacyProtocol, new OctetString(privacyPassphrase)); 607 + DefaultMOTable ifTable =
  608 + new DefaultMOTable(new OID("1.3.6.1.2.1.2.2.1"), indexDef, columns);
  609 + MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
  610 + Variable[] rowValues1 = new Variable[] {
  611 + new Integer32(1),
  612 + new OctetString("eth0"),
  613 + new Integer32(6),
  614 + new Integer32(1500),
  615 + new Gauge32(100000000),
  616 + new OctetString("00:00:00:00:01"),
  617 + new Integer32(1),
  618 + new Integer32(1)
  619 + };
  620 + Variable[] rowValues2 = new Variable[] {
  621 + new Integer32(2),
  622 + new OctetString("loopback"),
  623 + new Integer32(24),
  624 + new Integer32(1500),
  625 + new Gauge32(10000000),
  626 + new OctetString("00:00:00:00:02"),
  627 + new Integer32(1),
  628 + new Integer32(1)
  629 + };
  630 + model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
  631 + model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
  632 + ifTable.setVolatile(true);
  633 + return ifTable;
42 } 634 }
43 635
44 - public void initV3(UsmUser user, String securityName) {  
45 -// snmp.getUSM().addUser(user); 636 + private static DefaultMOTable createStaticSnmp4sTable() {
  637 + MOTableSubIndex[] subIndexes =
  638 + new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
  639 + MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
  640 + MOColumn[] columns = new MOColumn[8];
  641 + int c = 0;
  642 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_NULL, MOAccessImpl.ACCESS_READ_ONLY); // testNull
  643 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // testBoolean
  644 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifType
  645 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
  646 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
  647 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY); //ifPhysAddress
  648 + columns[c++] = new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_WRITE,
  649 + null);
  650 + // ifAdminStatus
  651 + columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY);
  652 + // ifOperStatus
  653 +
  654 + DefaultMOTable ifTable =
  655 + new DefaultMOTable(new OID("1.3.6.1.4.1.50000.1.1"), indexDef, columns);
  656 + MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
  657 + Variable[] rowValues1 = new Variable[] {
  658 + new Integer32(1),
  659 + new OctetString("eth0"),
  660 + new Integer32(6),
  661 + new Integer32(1500),
  662 + new Gauge32(100000000),
  663 + new OctetString("00:00:00:00:01"),
  664 + new Integer32(1),
  665 + new Integer32(1)
  666 + };
  667 + Variable[] rowValues2 = new Variable[] {
  668 + new Integer32(2),
  669 + new OctetString("loopback"),
  670 + new Integer32(24),
  671 + new Integer32(1500),
  672 + new Gauge32(10000000),
  673 + new OctetString("00:00:00:00:02"),
  674 + new Integer32(1),
  675 + new Integer32(1)
  676 + };
  677 + model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
  678 + model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
  679 + ifTable.setVolatile(true);
  680 + return ifTable;
  681 + }
  682 +
  683 + protected void initTransportMappings() throws IOException {
  684 + transportMappings = new TransportMapping[2];
  685 + Address addr = GenericAddress.parse(address);
  686 + TransportMapping tm =
  687 + TransportMappings.getInstance().createTransportMapping(addr);
  688 + transportMappings[0] = tm;
  689 + transportMappings[1] = new DefaultTcpTransportMapping(new TcpAddress(address));
  690 + }
  691 +
  692 + public void start(String ip, String port) throws IOException {
  693 + address = ip + "/" + port;
  694 + //BasicConfigurator.configure();
  695 + init();
  696 + addShutdownHook();
  697 +// loadConfig(ImportModes.REPLACE_CREATE);
  698 + getServer().addContext(new OctetString("public"));
  699 + finishInit();
  700 + run();
  701 + sendColdStartNotification();
  702 + }
  703 +
  704 + protected void unregisterManagedObjects() {
  705 + // here we should unregister those objects previously registered...
  706 + }
  707 +
  708 + protected void addCommunities(SnmpCommunityMIB communityMIB) {
  709 + Variable[] com2sec = new Variable[] {
  710 + new OctetString("public"), // community name
  711 + new OctetString("cpublic"), // security name
  712 + getAgent().getContextEngineID(), // local engine ID
  713 + new OctetString("public"), // default context name
  714 + new OctetString(), // transport tag
  715 + new Integer32(StorageType.nonVolatile), // storage type
  716 + new Integer32(RowStatus.active) // row status
  717 + };
  718 + MOTableRow row =
  719 + communityMIB.getSnmpCommunityEntry().createRow(
  720 + new OctetString("public2public").toSubIndex(true), com2sec);
  721 + communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row);
  722 +// snmpCommunityMIB.setSourceAddressFiltering(true);
  723 + }
  724 +
  725 + protected void registerSnmpMIBs() {
  726 + heartbeatMIB = new Snmp4jHeartbeatMib(super.getNotificationOriginator(),
  727 + new OctetString(),
  728 + super.snmpv2MIB.getSysUpTime());
  729 + agentppSimulationMIB = new AgentppSimulationMib();
  730 + super.registerSnmpMIBs();
  731 + }
46 732
47 - UserTarget userTarget = new UserTarget();  
48 - userTarget.setSecurityName(new OctetString(securityName));  
49 - userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV); 733 + protected void initMessageDispatcher() {
  734 + this.dispatcher = new MessageDispatcherImpl();
  735 + this.mpv3 = new MPv3(this.agent.getContextEngineID().getValue());
  736 + this.usm = new USM(SecurityProtocols.getInstance(), this.agent.getContextEngineID(), this.updateEngineBoots());
  737 + SecurityModels.getInstance().addSecurityModel(this.usm);
  738 + SecurityProtocols.getInstance().addDefaultProtocols();
  739 + this.dispatcher.addMessageProcessingModel(new MPv1());
  740 + this.dispatcher.addMessageProcessingModel(new MPv2c());
  741 + this.dispatcher.addMessageProcessingModel(this.mpv3);
  742 + this.initSnmpSession();
50 } 743 }
51 744
52 -} 745 +}
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java renamed from common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java
@@ -19,7 +19,7 @@ import java.io.IOException; @@ -19,7 +19,7 @@ import java.io.IOException;
19 import java.util.Map; 19 import java.util.Map;
20 import java.util.Scanner; 20 import java.util.Scanner;
21 21
22 -public class SnmpTest { 22 +public class SnmpTestV2 {
23 public static void main(String[] args) throws IOException { 23 public static void main(String[] args) throws IOException {
24 SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public"); 24 SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
25 25
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  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 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import org.snmp4j.CommandResponderEvent;
  19 +import org.snmp4j.agent.CommandProcessor;
  20 +import org.snmp4j.mp.MPv3;
  21 +import org.snmp4j.smi.OctetString;
  22 +
  23 +import java.io.IOException;
  24 +import java.util.Map;
  25 +import java.util.Scanner;
  26 +
  27 +public class SnmpTestV3 {
  28 + public static void main(String[] args) throws IOException {
  29 + SnmpDeviceSimulatorV3 device = new SnmpDeviceSimulatorV3(new CommandProcessor(new OctetString(MPv3.createLocalEngineID())) {
  30 + @Override
  31 + public void processPdu(CommandResponderEvent event) {
  32 + System.out.println("event: " + event);
  33 + }
  34 + });
  35 + device.start("0.0.0.0", "1610");
  36 +
  37 + device.setUpMappings(Map.of(
  38 + ".1.3.6.1.2.1.1.1.50", "12",
  39 + ".1.3.6.1.2.1.2.1.52", "56",
  40 + ".1.3.6.1.2.1.3.1.54", "yes",
  41 + ".1.3.6.1.2.1.7.1.58", ""
  42 + ));
  43 +
  44 + new Scanner(System.in).nextLine();
  45 + }
  46 +}
@@ -38,16 +38,6 @@ @@ -38,16 +38,6 @@
38 "dataType": "STRING" 38 "dataType": "STRING"
39 } 39 }
40 ] 40 ]
41 - },  
42 - {  
43 - "spec": "TELEMETRY_TRAPS_RECEIVING",  
44 - "mappings": [  
45 - {  
46 - "oid": ".1.3.6.1.2.8.7.1.56",  
47 - "key": "temperature_trap",  
48 - "dataType": "LONG"  
49 - }  
50 - ]  
51 } 41 }
52 ] 42 ]
53 } 43 }
  1 +{
  2 + "address": "192.168.3.23",
  3 + "port": 1610,
  4 + "protocolVersion": "V3",
  5 +
  6 + "username": "tb-user",
  7 + "engineId": "qwertyuioa",
  8 + "securityName": "tb-user",
  9 + "authenticationProtocol": "SHA_512",
  10 + "authenticationPassphrase": "sdfghjkloifgh",
  11 + "privacyProtocol": "DES",
  12 + "privacyPassphrase": "rtytguijokod"
  13 +}
1 { 1 {
2 "address": "127.0.0.1", 2 "address": "127.0.0.1",
3 "port": 1610, 3 "port": 1610,
4 - "password": "public", 4 + "community": "public",
5 "protocolVersion": "V2C" 5 "protocolVersion": "V2C"
6 } 6 }
@@ -558,6 +558,14 @@ public class JsonConverter { @@ -558,6 +558,14 @@ public class JsonConverter {
558 } 558 }
559 } 559 }
560 560
  561 + public static JsonElement parse(String json) {
  562 + return JSON_PARSER.parse(json);
  563 + }
  564 +
  565 + public static String toJson(JsonElement element) {
  566 + return GSON.toJson(element);
  567 + }
  568 +
561 public static void setTypeCastEnabled(boolean enabled) { 569 public static void setTypeCastEnabled(boolean enabled) {
562 isTypeCastEnabled = enabled; 570 isTypeCastEnabled = enabled;
563 } 571 }
@@ -599,8 +607,7 @@ public class JsonConverter { @@ -599,8 +607,7 @@ public class JsonConverter {
599 .build(); 607 .build();
600 } 608 }
601 609
602 - private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String  
603 - provisionKey, String provisionSecret) { 610 + private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) {
604 return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder() 611 return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder()
605 .setProvisionDeviceKey(provisionKey) 612 .setProvisionDeviceKey(provisionKey)
606 .setProvisionDeviceSecret(provisionSecret) 613 .setProvisionDeviceSecret(provisionSecret)