Browse Source

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

Improvements for SNMP: SNMP v3 security, RPC, refactoring
pull/4487/head
Andrew Shvayka 6 years ago
committed by GitHub
parent
commit
4ce818074b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 47
      common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
  2. 15
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
  3. 45
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java
  4. 43
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
  5. 21
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
  6. 7
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
  7. 20
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java
  8. 6
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java
  9. 32
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java
  10. 5
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java
  11. 10
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java
  12. 5
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java
  13. 25
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java
  14. 70
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
  15. 171
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java
  16. 121
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java
  17. 352
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
  18. 105
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
  19. 26
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
  20. 723
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
  21. 2
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java
  22. 46
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV3.java
  23. 10
      common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json
  24. 13
      common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json
  25. 2
      common/transport/snmp/src/test/resources/snmp-device-transport-config.json
  26. 11
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java

47
common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java

@ -17,18 +17,39 @@ package org.thingsboard.server.common.data.device.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.ToString;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import java.util.Objects;
@Data
@ToString(of = {"host", "port", "protocolVersion"})
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
private String address;
private int port;
private String host;
private Integer port;
private SnmpProtocolVersion protocolVersion;
/*
* For SNMP v1 and v2c
* */
private String community;
/*
* For SNMP v3
* */
private String username;
private String securityName;
private String authenticationPassphrase; // for SNMP v3
private String privacyPassphrase; // for SNMP v3
private String contextName;
private AuthenticationProtocol authenticationProtocol;
private String authenticationPassphrase;
private PrivacyProtocol privacyProtocol;
private String privacyPassphrase;
private String engineId;
@Override
public DeviceTransportType getType() {
@ -44,7 +65,21 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
@JsonIgnore
private boolean isValid() {
return StringUtils.isNotBlank(address) && port > 0 &&
StringUtils.isNotBlank(securityName) && protocolVersion != null;
boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null;
if (isValid) {
switch (protocolVersion) {
case V1:
case V2C:
isValid = StringUtils.isNotEmpty(community);
break;
case V3:
isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
&& contextName != null && authenticationProtocol != null
&& StringUtils.isNotBlank(authenticationPassphrase)
&& privacyProtocol != null && privacyPassphrase != null && engineId != null;
break;
}
}
return isValid;
}
}

15
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java

@ -18,14 +18,15 @@ package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import java.util.List;
@Data
public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
private int timeoutMs;
private int retries;
private Integer timeoutMs;
private Integer retries;
private List<SnmpCommunicationConfig> communicationConfigs;
@Override
@ -36,12 +37,16 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra
@Override
public void validate() {
if (!isValid()) {
throw new IllegalArgumentException("Transport configuration is not valid");
throw new IllegalArgumentException("SNMP transport configuration is not valid");
}
}
@JsonIgnore
private boolean isValid() {
return true;
return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
&& communicationConfigs != null
&& communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
&& communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid)
.distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum();
}
}

45
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
import java.util.Arrays;
import java.util.Optional;
public enum AuthenticationProtocol {
SHA_1("1.3.6.1.6.3.10.1.1.3"),
SHA_224("1.3.6.1.6.3.10.1.1.4"),
SHA_256("1.3.6.1.6.3.10.1.1.5"),
SHA_384("1.3.6.1.6.3.10.1.1.6"),
SHA_512("1.3.6.1.6.3.10.1.1.7"),
MD5("1.3.6.1.6.3.10.1.1.2");
// oids taken from org.snmp4j.security.SecurityProtocol implementations
private final String oid;
AuthenticationProtocol(String oid) {
this.oid = oid;
}
public String getOid() {
return oid;
}
public static Optional<AuthenticationProtocol> forName(String name) {
return Arrays.stream(values())
.filter(protocol -> protocol.name().equalsIgnoreCase(name))
.findFirst();
}
}

43
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
import java.util.Arrays;
import java.util.Optional;
public enum PrivacyProtocol {
DES("1.3.6.1.6.3.10.1.2.2"),
AES_128("1.3.6.1.6.3.10.1.2.4"),
AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"),
AES_256("1.3.6.1.4.1.4976.2.2.1.1.2");
// oids taken from org.snmp4j.security.SecurityProtocol implementations
private final String oid;
PrivacyProtocol(String oid) {
this.oid = oid;
}
public String getOid() {
return oid;
}
public static Optional<PrivacyProtocol> forName(String name) {
return Arrays.stream(values())
.filter(protocol -> protocol.name().equalsIgnoreCase(name))
.findFirst();
}
}

21
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java

@ -16,25 +16,10 @@
package org.thingsboard.server.common.data.transport.snmp;
public enum SnmpCommunicationSpec {
TELEMETRY_QUERYING(true),
CLIENT_ATTRIBUTES_QUERYING(true),
TELEMETRY_QUERYING,
CLIENT_ATTRIBUTES_QUERYING,
SHARED_ATTRIBUTES_SETTING,
TELEMETRY_TRAPS_RECEIVING,
CLIENT_ATTRIBUTES_TRAPS_RECEIVING;
private final boolean isRepeatingQuerying;
SnmpCommunicationSpec() {
this.isRepeatingQuerying = false;
}
SnmpCommunicationSpec(boolean isRepeatingQuerying) {
this.isRepeatingQuerying = isRepeatingQuerying;
}
public boolean isRepeatingQuerying() {
return isRepeatingQuerying;
}
TO_DEVICE_RPC_REQUEST,
}

7
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java

@ -16,13 +16,17 @@
package org.thingsboard.server.common.data.transport.snmp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.kv.DataType;
import java.util.regex.Pattern;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SnmpMapping {
private String oid;
private String key;
@ -32,7 +36,6 @@ public class SnmpMapping {
@JsonIgnore
public boolean isValid() {
return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() &&
StringUtils.isNotBlank(key) && dataType != null;
return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key);
}
}

20
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryTrapsReceivingSnmpCommunicationConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java

@ -13,18 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
package org.thingsboard.server.common.data.transport.snmp.config;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import lombok.Data;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import java.util.List;
@Data
public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig {
protected List<SnmpMapping> mappings;
public class TelemetryTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING;
public boolean isValid() {
return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
}
@Override
public boolean isValid() {
return false;
public List<SnmpMapping> getAllMappings() {
return mappings;
}
}

6
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/RepeatingQueryingSnmpCommunicationConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
package org.thingsboard.server.common.data.transport.snmp.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
@EqualsAndHashCode(callSuper = true)
@Data
public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommunicationConfig {
public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
private Long queryingFrequencyMs;
@Override
@ -31,6 +31,6 @@ public abstract class RepeatingQueryingSnmpCommunicationConfig extends SnmpCommu
@Override
public boolean isValid() {
return true;
return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid();
}
}

32
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SnmpCommunicationConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
package org.thingsboard.server.common.data.transport.snmp.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@ -23,6 +23,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
import java.util.List;
@ -31,32 +34,21 @@ import java.util.List;
@JsonSubTypes({
@Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
@Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
@Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"),
@Type(value = TelemetryTrapsReceivingSnmpCommunicationConfig.class, name = "TELEMETRY_TRAPS_RECEIVING"),
@Type(value = ClientAttributesTrapsReceivingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_TRAPS_RECEIVING")
@Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING")
})
public abstract class SnmpCommunicationConfig {
protected List<SnmpMapping> mappings;
public interface SnmpCommunicationConfig {
public List<SnmpMapping> getMappings() {
return mappings;
}
public abstract SnmpCommunicationSpec getSpec();
SnmpCommunicationSpec getSpec();
@JsonIgnore
public SnmpMethod getMethod() {
default SnmpMethod getMethod() {
return null;
}
@JsonIgnore
public boolean isValid() {
return true;
}
List<SnmpMapping> getAllMappings();
@JsonIgnore
boolean isValid();
public void validate() {
if (!isValid()) {
throw new IllegalArgumentException("Communication config is not valid");
}
}
}

5
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesQueryingSnmpCommunicationConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java

@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
}
}

10
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/SharedAttributesSettingSnmpCommunicationConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java

@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
@ -29,8 +31,4 @@ public class SharedAttributesSettingSnmpCommunicationConfig extends SnmpCommunic
return SnmpMethod.SET;
}
@Override
public boolean isValid() {
return true;
}
}

5
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/TelemetryQueryingSnmpCommunicationConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java

@ -13,17 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
@EqualsAndHashCode(callSuper = true)
@Data
public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.TELEMETRY_QUERYING;
}
}

25
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/configs/ClientAttributesTrapsReceivingSnmpCommunicationConfig.java

@ -1,25 +0,0 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.configs;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
public class ClientAttributesTrapsReceivingSnmpCommunicationConfig extends SnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING;
}
}

70
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java

@ -15,9 +15,9 @@
*/
package org.thingsboard.server.transport.snmp;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
@ -42,6 +42,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
import org.thingsboard.server.transport.snmp.service.SnmpAuthService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
@ -61,18 +62,18 @@ import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
public class SnmpTransportContext extends TransportContext {
@Getter
private final SnmpTransportService snmpTransportService;
private final TransportDeviceProfileCache deviceProfileCache;
private final TransportService transportService;
private final ProtoTransportEntityService protoEntityService;
private final SnmpTransportBalancingService balancingService;
@Getter
private final SnmpAuthService snmpAuthService;
private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>();
private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
@AfterStartUp(order = 2)
public void initDevicesSessions() {
log.info("Initializing SNMP devices sessions");
@ -89,18 +90,12 @@ public class SnmpTransportContext extends TransportContext {
managedDevicesIds.stream()
.map(protoEntityService::getDeviceById)
.collect(Collectors.toList())
.forEach(device -> {
try {
establishDeviceSession(device);
} catch (Exception e) {
log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.getMessage());
}
});
.forEach(this::establishDeviceSession);
}
private void establishDeviceSession(Device device) {
if (device == null) return;
log.info("Establishing SNMP device session for device {}", device.getId());
log.info("Establishing SNMP session for device {}", device.getId());
DeviceProfileId deviceProfileId = device.getDeviceProfileId();
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
@ -114,19 +109,24 @@ public class SnmpTransportContext extends TransportContext {
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
DeviceSessionContext deviceSessionContext = new DeviceSessionContext(
device, deviceProfile, credentials.getCredentialsId(),
profileTransportConfiguration, deviceTransportConfiguration,
this, snmpTransportService, snmpUnderlyingProtocol
);
registerSessionMsgListener(deviceSessionContext);
DeviceSessionContext deviceSessionContext;
try {
deviceSessionContext = new DeviceSessionContext(
device, deviceProfile, credentials.getCredentialsId(),
profileTransportConfiguration, deviceTransportConfiguration, this
);
registerSessionMsgListener(deviceSessionContext);
} catch (Exception e) {
log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString());
return;
}
sessions.put(device.getId(), deviceSessionContext);
snmpTransportService.createQueryingTasks(deviceSessionContext);
log.info("Established SNMP device session for device {}", device.getId());
}
private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
log.info("Updating SNMP device session for device {}", device.getId());
log.info("Updating SNMP session for device {}", device.getId());
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
@ -138,16 +138,21 @@ public class SnmpTransportContext extends TransportContext {
SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
snmpTransportService.cancelQueryingTasks(sessionContext);
snmpTransportService.createQueryingTasks(sessionContext);
} else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
} else {
log.trace("Configuration of the device {} was not updated", device);
try {
if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
snmpTransportService.cancelQueryingTasks(sessionContext);
snmpTransportService.createQueryingTasks(sessionContext);
} else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
} else {
log.trace("Configuration of the device {} was not updated", device);
}
} catch (Exception e) {
log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage());
destroyDeviceSession(sessionContext);
}
}
@ -155,9 +160,10 @@ public class SnmpTransportContext extends TransportContext {
if (sessionContext == null) return;
log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
sessionContext.close();
snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
transportService.deregisterSession(sessionContext.getSessionInfo());
sessions.remove(sessionContext.getDeviceId());
snmpTransportService.cancelQueryingTasks(sessionContext);
sessions.remove(sessionContext.getDeviceId());
log.trace("Unregistered and removed session");
}
@ -173,7 +179,9 @@ public class SnmpTransportContext extends TransportContext {
);
transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
deviceSessionContext.setSessionInfo(sessionInfo);
deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
} else {

171
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java

@ -0,0 +1,171 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@TbSnmpTransportComponent
@Service
@Slf4j
public class PduService {
public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
PDU pdu = setUpPdu(sessionContext);
pdu.setType(communicationConfig.getMethod().getCode());
pdu.addAll(communicationConfig.getAllMappings().stream()
.filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
.map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
.map(value -> {
Variable variable = toSnmpVariable(value, mapping.getDataType());
return new VariableBinding(new OID(mapping.getOid()), variable);
})
.orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
.collect(Collectors.toList()));
return pdu;
}
public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) {
PDU pdu = setUpPdu(sessionContext);
pdu.setType(snmpMethod.getCode());
Variable variable = value == null ? Null.instance : toSnmpVariable(value, dataType);
pdu.add(new VariableBinding(new OID(oid), variable));
return pdu;
}
private Variable toSnmpVariable(String value, DataType dataType) {
dataType = dataType == null ? DataType.STRING : dataType;
Variable variable;
switch (dataType) {
case LONG:
try {
variable = new Integer32(Integer.parseInt(value));
break;
} catch (NumberFormatException ignored) {
}
case DOUBLE:
case BOOLEAN:
case STRING:
case JSON:
default:
variable = new OctetString(value);
}
return variable;
}
private PDU setUpPdu(DeviceSessionContext sessionContext) {
PDU pdu;
SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
switch (snmpVersion) {
case V1:
case V2C:
pdu = new PDU();
break;
case V3:
ScopedPDU scopedPdu = new ScopedPDU();
scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
pdu = scopedPdu;
break;
default:
throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
}
return pdu;
}
public JsonObject processPdu(PDU pdu, List<SnmpMapping> responseMappings) {
Map<OID, String> values = processPdu(pdu);
Map<OID, SnmpMapping> mappings = new HashMap<>();
if (responseMappings != null) {
for (SnmpMapping mapping : responseMappings) {
OID oid = new OID(mapping.getOid());
mappings.put(oid, mapping);
}
}
JsonObject data = new JsonObject();
values.forEach((oid, value) -> {
log.trace("Processing variable binding: {} - {}", oid, value);
SnmpMapping mapping = mappings.get(oid);
if (mapping == null) {
log.debug("No SNMP mapping for oid {}", oid);
return;
}
processValue(mapping.getKey(), mapping.getDataType(), value, data);
});
return data;
}
public Map<OID, String> processPdu(PDU pdu) {
return IntStream.range(0, pdu.size())
.mapToObj(pdu::get)
.filter(Objects::nonNull)
.filter(variableBinding -> !(variableBinding.getVariable() instanceof Null))
.collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString));
}
private void processValue(String key, DataType dataType, String value, JsonObject result) {
switch (dataType) {
case LONG:
result.addProperty(key, Long.parseLong(value));
break;
case BOOLEAN:
result.addProperty(key, Boolean.parseBoolean(value));
break;
case DOUBLE:
result.addProperty(key, Double.parseDouble(value));
break;
case STRING:
case JSON:
default:
result.addProperty(key, value);
}
}
}

121
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java

@ -0,0 +1,121 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import lombok.RequiredArgsConstructor;
import org.snmp4j.AbstractTarget;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
import org.snmp4j.UserTarget;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import java.util.Optional;
@Service
@TbSnmpTransportComponent
@RequiredArgsConstructor
public class SnmpAuthService {
private final SnmpTransportService snmpTransportService;
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
AbstractTarget target;
SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
switch (protocolVersion) {
case V1:
CommunityTarget communityTargetV1 = new CommunityTarget();
communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
target = communityTargetV1;
break;
case V2C:
CommunityTarget communityTargetV2 = new CommunityTarget();
communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
target = communityTargetV2;
break;
case V3:
OctetString username = new OctetString(deviceTransportConfig.getUsername());
OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
OctetString engineId = new OctetString(deviceTransportConfig.getEngineId());
OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid());
OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid());
OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue()));
OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue()));
USM usm = snmpTransportService.getSnmp().getUSM();
if (usm.hasUser(engineId, securityName)) {
usm.removeAllUsers(username, engineId);
}
usm.addLocalizedUser(
engineId.getValue(), username,
authenticationProtocol, authenticationPassphrase.getValue(),
privacyProtocol, privacyPassphrase.getValue()
);
UserTarget userTarget = new UserTarget();
userTarget.setSecurityName(securityName);
userTarget.setAuthoritativeEngineID(engineId.getValue());
userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target = userTarget;
break;
default:
throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
}
Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort());
target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid")));
target.setTimeout(profileTransportConfig.getTimeoutMs());
target.setRetries(profileTransportConfig.getRetries());
target.setVersion(protocolVersion.getCode());
return target;
}
public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) {
SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) {
OctetString username = new OctetString(deviceTransportConfiguration.getUsername());
OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId());
snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId);
}
}
}

352
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java

@ -15,48 +15,46 @@
*/
package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.mp.MPv3;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.configs.RepeatingQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.configs.SnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -65,44 +63,36 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@TbSnmpTransportComponent
@Service
@Slf4j
public class SnmpTransportService implements TbTransportService, CommandResponder {
private final SnmpTransportContext snmpTransportContext;
@RequiredArgsConstructor
public class SnmpTransportService implements TbTransportService {
private final TransportService transportService;
private final PduService pduService;
@Getter
private Snmp snmp;
private ScheduledExecutorService queryingExecutor;
private ExecutorService responseProcessingExecutor;
private final Map<SnmpCommunicationSpec, BiConsumer<JsonObject, DeviceSessionContext>> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class);
private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
@Value("${transport.snmp.response_processing.parallelism_level}")
private Integer responseProcessingParallelismLevel;
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
public SnmpTransportService(@Lazy SnmpTransportContext snmpTransportContext,
TransportService transportService) {
this.snmpTransportContext = snmpTransportContext;
this.transportService = transportService;
}
@PostConstruct
private void init() throws IOException {
log.info("Initializing SNMP transport service");
queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
initializeSnmp();
initializeTrapsListener();
configureResponseDataMappers();
configureResponseProcessors();
log.info("SNMP transport service initialized");
@ -122,53 +112,30 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
snmp = new Snmp(transportMapping);
snmp.listen();
}
private void initializeTrapsListener() throws IOException {
int trapsListeningPort = 1062;
String bindingAddress = "0.0.0.0/" + trapsListeningPort;
TransportMapping<?> transportMapping;
switch (snmpUnderlyingProtocol) {
case "udp":
transportMapping = new DefaultUdpTransportMapping(new UdpAddress(bindingAddress));
break;
case "tcp":
transportMapping = new DefaultTcpTransportMapping(new TcpAddress(bindingAddress));
break;
default:
throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
}
Snmp trapsSnmp = new Snmp(transportMapping);
trapsSnmp.addCommandResponder(this);
transportMapping.listen();
USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
}
public void createQueryingTasks(DeviceSessionContext sessionContext) {
List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
.filter(config -> config.getSpec().isRepeatingQuerying())
.filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig)
.map(config -> {
RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
return createQueryingTaskForConfig(sessionContext, repeatingCommunicationConfig);
Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs();
return queryingExecutor.scheduleWithFixedDelay(() -> {
try {
if (sessionContext.isActive()) {
sendRequest(sessionContext, repeatingCommunicationConfig);
}
} catch (Exception e) {
log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
}
}, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
})
.collect(Collectors.toList());
sessionContext.setQueryingTasks(queryingTasks);
}
private ScheduledFuture<?> createQueryingTaskForConfig(DeviceSessionContext sessionContext, RepeatingQueryingSnmpCommunicationConfig communicationConfig) {
Long queryingFrequency = communicationConfig.getQueryingFrequencyMs();
return queryingExecutor.scheduleWithFixedDelay(() -> {
try {
if (sessionContext.isActive()) {
sendRequest(sessionContext, communicationConfig);
}
} catch (Exception e) {
log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.getMessage());
}
}, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
sessionContext.getQueryingTasks().addAll(queryingTasks);
}
public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
@ -176,187 +143,146 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
sessionContext.getQueryingTasks().clear();
}
public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) throws IOException {
PDU request = createPdu(communicationConfig);
executeRequest(sessionContext, request);
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
}
public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) throws IOException {
PDU request = createPduWithValues(communicationConfig, values);
executeRequest(sessionContext, request);
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
PDU request = pduService.createPdu(sessionContext, communicationConfig, values);
RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings());
sendRequest(sessionContext, request, requestInfo);
}
private void executeRequest(DeviceSessionContext sessionContext, PDU request) throws IOException {
private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) {
if (request.size() > 0) {
log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
snmp.send(request, sessionContext.getTarget(), sessionContext.getDeviceProfile().getId(), sessionContext);
try {
snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext);
} catch (IOException e) {
log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString());
}
}
}
private PDU createPdu(SnmpCommunicationConfig communicationConfig) {
PDU pdu = new PDU();
pdu.setType(communicationConfig.getMethod().getCode());
pdu.addAll(communicationConfig.getMappings().stream()
.map(mapping -> new VariableBinding(new OID(mapping.getOid())))
.collect(Collectors.toList()));
return pdu;
public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) {
sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
.filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
.findFirst()
.ifPresent(communicationConfig -> {
Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
));
sendRequest(sessionContext, communicationConfig, sharedAttributes);
});
}
private PDU createPduWithValues(SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
PDU pdu = new PDU();
pdu.setType(communicationConfig.getMethod().getCode());
pdu.addAll(communicationConfig.getMappings().stream()
.filter(mapping -> values.containsKey(mapping.getKey()))
.map(mapping -> {
String value = values.get(mapping.getKey());
return new VariableBinding(new OID(mapping.getOid()), new OctetString(value));
})
.collect(Collectors.toList()));
return pdu;
}
public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) {
SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName());
JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject();
String oid = Optional.ofNullable(params.get("oid")).map(JsonElement::getAsString).orElse(null);
String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null);
DataType dataType = Optional.ofNullable(params.get("dataType")).map(e -> DataType.valueOf(e.getAsString())).orElse(DataType.STRING);
private void processTrap(CommandResponderEvent event) {
if (event.getPDU().getType() != PDU.TRAP) return;
if (oid == null || oid.isEmpty()) {
throw new IllegalArgumentException("OID in to-device RPC request is not specified");
}
if (value == null && snmpMethod == SnmpMethod.SET) {
throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'");
}
snmpTransportContext.getSessions().stream()
.filter(sessionContext -> {
// TODO: SNMP v3 support
return sessionContext.getTarget().getSecurityName().equals(OctetString.fromByteArray(event.getSecurityName())) &&
sessionContext.getTarget().getAddress().equals(event.getPeerAddress());
})
.findFirst()
.ifPresentOrElse(sessionContext -> {
responseProcessingExecutor.execute(() -> processResponse(sessionContext, event.getPDU()));
}, () -> {
log.debug("SNMP event is from unknown source: {}", event);
});
PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType);
sendRequest(sessionContext, request, new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST));
}
public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
log.warn("SNMP response error: {}", event.getError().toString());
return;
}
PDU response = event.getResponse();
if (response == null) {
log.warn("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
return;
}
DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
log.debug("[{}] Processing SNMP response for device {} with device profile {}: {}",
response.getRequestID(), sessionContext.getDeviceId(), deviceProfileId, response);
responseProcessingExecutor.execute(() -> processResponse(sessionContext, response));
RequestInfo requestInfo = (RequestInfo) event.getUserObject();
responseProcessingExecutor.execute(() -> {
processResponse(sessionContext, response, requestInfo);
});
}
private void processResponse(DeviceSessionContext sessionContext, PDU responsePdu) {
Map<OID, SnmpMapping> mappings = new HashMap<>();
Map<OID, SnmpCommunicationConfig> configs = new HashMap<>();
Map<SnmpCommunicationSpec, JsonObject> responses = new EnumMap<>(SnmpCommunicationSpec.class);
private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) {
ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec());
if (responseProcessor == null) return;
for (SnmpCommunicationConfig config : sessionContext.getProfileTransportConfiguration().getCommunicationConfigs()) {
for (SnmpMapping mapping : config.getMappings()) {
OID oid = new OID(mapping.getOid());
mappings.put(oid, mapping);
configs.put(oid, config);
}
responses.put(config.getSpec(), new JsonObject());
}
for (int i = 0; i < responsePdu.size(); i++) {
VariableBinding variableBinding = responsePdu.get(i);
log.trace("Processing variable binding {}: {}", i, variableBinding);
if (variableBinding.getVariable() instanceof Null) {
log.debug("Response variable is empty");
continue;
}
JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo);
OID oid = variableBinding.getOid();
if (!mappings.containsKey(oid)) {
log.debug("No SNMP mapping for oid {}", oid);
continue;
}
SnmpCommunicationSpec spec = configs.get(oid).getSpec();
if (!responseProcessors.containsKey(spec)) {
log.debug("No response processor found for spec {}", spec);
continue;
}
SnmpMapping mapping = mappings.get(oid);
processValue(mapping.getKey(), mapping.getDataType(), variableBinding.toValueString(), responses.get(spec));
}
if (responses.values().stream().allMatch(response -> response.entrySet().isEmpty())) {
log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), responsePdu.getRequestID());
if (responseData.entrySet().isEmpty()) {
log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID());
return;
}
responses.forEach((spec, response) -> {
Optional.ofNullable(responseProcessors.get(spec))
.ifPresent(responseProcessor -> {
responseProcessor.accept(response, sessionContext);
});
responseProcessor.process(responseData, requestInfo, sessionContext);
reportActivity(sessionContext.getSessionInfo());
}
private void configureResponseDataMappers() {
responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> {
JsonObject responseData = new JsonObject();
pduService.processPdu(pdu).forEach((oid, value) -> {
responseData.addProperty(oid.toDottedString(), value);
});
return responseData;
});
reportActivity(sessionContext.getSessionInfo());
ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> {
return pduService.processPdu(pdu, requestInfo.getResponseMappings());
};
Arrays.stream(SnmpCommunicationSpec.values())
.forEach(communicationSpec -> {
responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper);
});
}
private void configureResponseProcessors() {
Stream.of(SnmpCommunicationSpec.TELEMETRY_QUERYING, SnmpCommunicationSpec.TELEMETRY_TRAPS_RECEIVING)
.forEach(telemetrySpec -> {
responseProcessors.put(telemetrySpec, (response, sessionContext) -> {
TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(response);
transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, TransportServiceCallback.EMPTY);
log.debug("Posted telemetry for device {}: {}", sessionContext.getDeviceId(), response);
});
});
responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> {
TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData);
transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null);
log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
});
Stream.of(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, SnmpCommunicationSpec.CLIENT_ATTRIBUTES_TRAPS_RECEIVING)
.forEach(clientAttributesSpec -> {
responseProcessors.put(clientAttributesSpec, (response, sessionContext) -> {
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(response);
transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, TransportServiceCallback.EMPTY);
log.debug("Posted attributes for device {}: {}", sessionContext.getDeviceId(), response);
});
});
responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> {
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData);
transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null);
log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
});
responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> {
TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
.setRequestId(requestInfo.getRequestId())
.setPayload(JsonConverter.toJson(responseData))
.build();
transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null);
log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId());
});
}
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(false)
.setRpcSubscription(false)
.setAttributeSubscription(true)
.setRpcSubscription(true)
.setLastActivityTime(System.currentTimeMillis())
.build(), TransportServiceCallback.EMPTY);
}
private void processValue(String key, DataType dataType, String value, JsonObject result) {
if (StringUtils.isEmpty(value)) return;
switch (dataType) {
case LONG:
result.addProperty(key, Long.parseLong(value));
break;
case BOOLEAN:
result.addProperty(key, Boolean.parseBoolean(value));
break;
case DOUBLE:
result.addProperty(key, Double.parseDouble(value));
break;
default:
result.addProperty(key, value);
}
}
@Override
public void processPdu(CommandResponderEvent event) {
processTrap(event);
}
@Override
public String getName() {
@ -381,4 +307,34 @@ public class SnmpTransportService implements TbTransportService, CommandResponde
}
log.info("SNMP transport stopped!");
}
@Data
private static class RequestInfo {
private Integer requestId;
private SnmpCommunicationSpec communicationSpec;
private List<SnmpMapping> responseMappings;
public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec) {
this.requestId = requestId;
this.communicationSpec = communicationSpec;
}
public RequestInfo(SnmpCommunicationSpec communicationSpec) {
this.communicationSpec = communicationSpec;
}
public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
this.communicationSpec = communicationSpec;
this.responseMappings = responseMappings;
}
}
private interface ResponseDataMapper {
JsonObject map(PDU pdu, RequestInfo requestInfo);
}
private interface ResponseProcessor {
void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext);
}
}

105
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java

@ -18,31 +18,15 @@ package org.thingsboard.server.transport.snmp.session;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.AbstractTarget;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
@ -51,15 +35,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Slf4j
public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener {
@ -76,25 +57,18 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
private final Device device;
private final SnmpTransportContext snmpTransportContext;
private final SnmpTransportService snmpTransportService;
@Getter
@Setter
private long previousRequestExecutedAt = 0;
private final AtomicInteger msgIdSeq = new AtomicInteger(0);
@Getter
private boolean isActive = true;
private final String snmpUnderlyingProtocol;
@Getter
@Setter
private List<ScheduledFuture<?>> queryingTasks = new LinkedList<>();
private final List<ScheduledFuture<?>> queryingTasks = new LinkedList<>();
public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
SnmpDeviceTransportConfiguration deviceTransportConfiguration,
SnmpTransportContext snmpTransportContext, SnmpTransportService snmpTransportService,
String snmpUnderlyingProtocol) {
SnmpTransportContext snmpTransportContext) throws Exception {
super(UUID.randomUUID());
super.setDeviceId(device.getId());
super.setDeviceProfile(deviceProfile);
@ -102,12 +76,10 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
this.token = token;
this.snmpTransportContext = snmpTransportContext;
this.snmpTransportService = snmpTransportService;
this.profileTransportConfiguration = profileTransportConfiguration;
this.deviceTransportConfiguration = deviceTransportConfiguration;
this.snmpUnderlyingProtocol = snmpUnderlyingProtocol;
initializeTarget(profileTransportConfiguration, deviceTransportConfiguration);
}
@ -127,62 +99,14 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onResponse(ResponseEvent event) {
if (isActive) {
snmpTransportService.processResponseEvent(this, event);
snmpTransportContext.getSnmpTransportService().processResponseEvent(this, event);
}
}
public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) throws Exception {
log.trace("Initializing target for SNMP session of device {}", device);
AbstractTarget target;
SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
switch (protocolVersion) {
case V1:
CommunityTarget communityTargetV1 = new CommunityTarget();
communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));
target = communityTargetV1;
break;
case V2C:
CommunityTarget communityTargetV2 = new CommunityTarget();
communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getSecurityName()));
target = communityTargetV2;
break;
case V3:
USM usm = new USM();
SecurityModels.getInstance().addSecurityModel(usm);
OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
OID authenticationProtocol = AuthSHA.ID;
OID privacyProtocol = PrivDES.ID; // FIXME: to config
UsmUser user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
snmpTransportService.getSnmp().getUSM().addUser(user);
UserTarget userTarget = new UserTarget();
userTarget.setSecurityName(securityName);
userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target = userTarget;
break;
default:
throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
}
target.setAddress(GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getAddress() + "/" + deviceTransportConfig.getPort()));
target.setTimeout(profileTransportConfig.getTimeoutMs());
target.setRetries(profileTransportConfig.getRetries());
target.setVersion(protocolVersion.getCode());
this.target = target;
log.info("SNMP target initialized: {}", target);
this.target = snmpTransportContext.getSnmpAuthService().setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
log.debug("SNMP target initialized: {}", target);
}
public void close() {
@ -204,21 +128,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
profileTransportConfiguration.getCommunicationConfigs().stream()
.filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
.findFirst()
.ifPresent(communicationConfig -> {
Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
));
try {
snmpTransportService.sendRequest(this, communicationConfig, sharedAttributes);
} catch (Exception e) {
log.error("Failed to send request with shared attributes to SNMP device {}: {}", getDeviceId(), e.getMessage());
}
});
snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification);
}
@Override
@ -227,6 +137,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
@Override
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
}
@Override

26
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java

@ -73,32 +73,6 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent {
}
}
public static void main(String[] args) throws IOException {
SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
device.start();
device.setUpMappings(Map.of(
".1.3.6.1.2.1.1.1.50", "12",
".1.3.6.1.2.1.2.1.52", "56",
".1.3.6.1.2.1.3.1.54", "yes",
".1.3.6.1.2.1.7.1.58", ""
));
// while (true) {
// new Scanner(System.in).nextLine();
// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
// System.out.println("sent");
// }
// Snmp snmp = new Snmp(device.transportMappings[0]);
// device.snmp.addCommandResponder(event -> {
// System.out.println(event);
// });
new Scanner(System.in).nextLine();
}
private final Target target;
private final Address address;

723
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java

@ -15,38 +15,731 @@
*/
package org.thingsboard.server.transport.snmp;
import org.snmp4j.UserTarget;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.BaseAgent;
import org.snmp4j.agent.CommandProcessor;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOGroup;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.agent.mo.DefaultMOMutableRow2PC;
import org.snmp4j.agent.mo.DefaultMOTable;
import org.snmp4j.agent.mo.MOAccessImpl;
import org.snmp4j.agent.mo.MOColumn;
import org.snmp4j.agent.mo.MOMutableColumn;
import org.snmp4j.agent.mo.MOMutableTableModel;
import org.snmp4j.agent.mo.MOScalar;
import org.snmp4j.agent.mo.MOTableIndex;
import org.snmp4j.agent.mo.MOTableRow;
import org.snmp4j.agent.mo.MOTableSubIndex;
import org.snmp4j.agent.mo.ext.AgentppSimulationMib;
import org.snmp4j.agent.mo.snmp.RowStatus;
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
import org.snmp4j.agent.mo.snmp.StorageType;
import org.snmp4j.agent.mo.snmp.TransportDomains;
import org.snmp4j.agent.mo.snmp.VacmMIB;
import org.snmp4j.agent.mo.snmp4j.example.Snmp4jHeartbeatMib;
import org.snmp4j.agent.security.MutableVACM;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.MessageProcessingModel;
import org.snmp4j.security.AuthHMAC192SHA256;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.PrivAES128;
import org.snmp4j.security.PrivAES192;
import org.snmp4j.security.PrivAES256;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.Gauge32;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.SMIConstants;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.TimeTicks;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.Variable;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.TransportMappings;
import org.snmp4j.util.ThreadPool;
import java.io.File;
import java.io.IOException;
import java.util.Map;
public class SnmpDeviceSimulatorV3 extends SnmpDeviceSimulatorV2 {
public SnmpDeviceSimulatorV3(int port, String securityName, String authenticationPassphrase, String privacyPassphrase) throws IOException {
super(12, null);
// super(new File("conf.agent"), new File("bootCounter.agent"));
/**
* The TestAgent is a sample SNMP agent implementation of all
* features (MIB implementations) provided by the SNMP4J-Agent framework.
*
* Note, for snmp4s, this code is mostly a copy from snmp4j.
* And don't remove snmp users
*
*/
public class SnmpDeviceSimulatorV3 extends BaseAgent {
protected String address;
private Snmp4jHeartbeatMib heartbeatMIB;
private AgentppSimulationMib agentppSimulationMIB;
public SnmpDeviceSimulatorV3(CommandProcessor processor) throws IOException {
super(new File("SNMP4JTestAgentBC.cfg"), new File("SNMP4JTestAgentConfig.cfg"),
processor);
agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
}
public void setUpMappings(Map<String, String> oidToResponseMappings) {
unregisterManagedObject(getSnmpv2MIB());
oidToResponseMappings.forEach((oid, response) -> {
registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
});
}
protected void registerManagedObject(ManagedObject mo) {
try {
server.register(mo, null);
} catch (DuplicateRegistrationException ex) {
throw new RuntimeException(ex);
}
}
protected void unregisterManagedObject(MOGroup moGroup) {
moGroup.unregisterMOs(server, getContext(moGroup));
}
protected void registerManagedObjects() {
try {
server.register(createStaticIfTable(), null);
server.register(createStaticIfXTable(), null);
agentppSimulationMIB.registerMOs(server, null);
heartbeatMIB.registerMOs(server, null);
} catch (DuplicateRegistrationException ex) {
ex.printStackTrace();
}
}
protected void addNotificationTargets(SnmpTargetMIB targetMIB,
SnmpNotificationMIB notificationMIB) {
targetMIB.addDefaultTDomains();
targetMIB.addTargetAddress(new OctetString("notificationV2c"),
TransportDomains.transportDomainUdpIpv4,
new OctetString(new UdpAddress("127.0.0.1/162").getValue()),
200, 1,
new OctetString("notify"),
new OctetString("v2c"),
StorageType.permanent);
targetMIB.addTargetAddress(new OctetString("notificationV3"),
TransportDomains.transportDomainUdpIpv4,
new OctetString(new UdpAddress("127.0.0.1/1162").getValue()),
200, 1,
new OctetString("notify"),
new OctetString("v3notify"),
StorageType.permanent);
targetMIB.addTargetParams(new OctetString("v2c"),
MessageProcessingModel.MPv2c,
SecurityModel.SECURITY_MODEL_SNMPv2c,
new OctetString("cpublic"),
SecurityLevel.AUTH_PRIV,
StorageType.permanent);
targetMIB.addTargetParams(new OctetString("v3notify"),
MessageProcessingModel.MPv3,
SecurityModel.SECURITY_MODEL_USM,
new OctetString("v3notify"),
SecurityLevel.NOAUTH_NOPRIV,
StorageType.permanent);
notificationMIB.addNotifyEntry(new OctetString("default"),
new OctetString("notify"),
SnmpNotificationMIB.SnmpNotifyTypeEnum.inform,
StorageType.permanent);
}
protected void addViews(VacmMIB vacm) {
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,
new OctetString("cpublic"),
new OctetString("v1v2group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c,
new OctetString("cpublic"),
new OctetString("v1v2group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHADES"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5DES"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("TEST"),
new OctetString("v3test"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHA"),
new OctetString("v3restricted"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHAAES128"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHAAES192"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHAAES256"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5AES128"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5AES192"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5AES256"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("aboba"),
new OctetString("v3group"),
StorageType.nonVolatile);
//============================================//
// agent5-auth-priv
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("agent5"),
new OctetString("v3group"),
StorageType.nonVolatile);
//===========================================//
// agent002
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("agent002"),
new OctetString("v3group"),
StorageType.nonVolatile);
//===========================================//
// user001-auth-no-priv
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("user001"),
new OctetString("group001"),
StorageType.nonVolatile);
//===========================================//
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("v3notify"),
new OctetString("v3group"),
StorageType.nonVolatile);
//===========================================//
// group auth no priv
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("v3notify-auth"),
new OctetString("group001"),
StorageType.nonVolatile);
//===========================================//
// my conf
vacm.addAccess(new OctetString("group001"), new OctetString("public"),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.AUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
SecurityModel.SECURITY_MODEL_ANY,
SecurityLevel.NOAUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3group"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.AUTH_PRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3restricted"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.NOAUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("restrictedReadView"),
new OctetString("restrictedWriteView"),
new OctetString("restrictedNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3test"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.AUTH_PRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("testReadView"),
new OctetString("testWriteView"),
new OctetString("testNotifyView"),
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedReadView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedWriteView"),
new OID("1.3.6.1.2.1"),
new OctetString(),
VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
new OID("1.3.6.1.6.3.1"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
USM usm = new USM();
SecurityModels.getInstance().addSecurityModel(usm);
vacm.addViewTreeFamily(new OctetString("testReadView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testReadView"),
new OID("1.3.6.1.2.1.1"),
new OctetString(), VacmMIB.vacmViewExcluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testWriteView"),
new OID("1.3.6.1.2.1"),
new OctetString(),
VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testNotifyView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
}
protected void addUsmUser(USM usm) {
UsmUser user = new UsmUser(new OctetString("SHADES"),
AuthSHA.ID,
new OctetString("SHADESAuthPassword"),
PrivDES.ID,
new OctetString("SHADESPrivPassword"));
// usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
usm.addUser(user.getSecurityName(), null, user);
user = new UsmUser(new OctetString("TEST"),
AuthSHA.ID,
new OctetString("maplesyrup"),
PrivDES.ID,
new OctetString("maplesyrup"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHA"),
AuthSHA.ID,
new OctetString("SHAAuthPassword"),
null,
null);
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHADES"),
AuthSHA.ID,
new OctetString("SHADESAuthPassword"),
PrivDES.ID,
new OctetString("SHADESPrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5DES"),
AuthMD5.ID,
new OctetString("MD5DESAuthPassword"),
PrivDES.ID,
new OctetString("MD5DESPrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHAAES128"),
AuthSHA.ID,
new OctetString("SHAAES128AuthPassword"),
PrivAES128.ID,
new OctetString("SHAAES128PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHAAES192"),
AuthSHA.ID,
new OctetString("SHAAES192AuthPassword"),
PrivAES192.ID,
new OctetString("SHAAES192PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHAAES256"),
AuthSHA.ID,
new OctetString("SHAAES256AuthPassword"),
PrivAES256.ID,
new OctetString("SHAAES256PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5AES128"),
AuthMD5.ID,
new OctetString("MD5AES128AuthPassword"),
PrivAES128.ID,
new OctetString("MD5AES128PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5AES192"),
AuthHMAC192SHA256.ID,
new OctetString("MD5AES192AuthPassword"),
PrivAES192.ID,
new OctetString("MD5AES192PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//==============================================================
user = new UsmUser(new OctetString("MD5AES256"),
AuthMD5.ID,
new OctetString("MD5AES256AuthPassword"),
PrivAES256.ID,
new OctetString("MD5AES256PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5AES256"),
AuthMD5.ID,
new OctetString("MD5AES256AuthPassword"),
PrivAES256.ID,
new OctetString("MD5AES256PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
OctetString securityName = new OctetString("aboba");
OctetString authenticationPassphrase = new OctetString("abobaaboba");
OctetString privacyPassphrase = new OctetString("abobaaboba");
OID authenticationProtocol = AuthSHA.ID;
OID privacyProtocol = PrivDES.ID;
OID privacyProtocol = PrivDES.ID; // FIXME: to config
user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
usm.addUser(user);
//===============================================================//
user = new UsmUser(new OctetString("agent5"),
AuthSHA.ID,
new OctetString("authpass"),
PrivDES.ID,
new OctetString("privpass"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//===============================================================//
// user001
user = new UsmUser(new OctetString("user001"),
AuthSHA.ID,
new OctetString("authpass"),
null, null);
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//===============================================================//
// user002
user = new UsmUser(new OctetString("user001"),
null,
null,
null, null);
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//===============================================================//
user = new UsmUser(new OctetString("v3notify"),
null,
null,
null,
null);
usm.addUser(user.getSecurityName(), null, user);
this.usm = usm;
}
private static DefaultMOTable createStaticIfXTable() {
MOTableSubIndex[] subIndexes =
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
MOColumn[] columns = new MOColumn[19];
int c = 0;
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
MOAccessImpl.ACCESS_READ_ONLY); // ifName
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifInMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifInBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifOutMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifOutBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInOctets
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInUcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutOctets
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutUcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_WRITE); // ifLinkUpDownTrapEnable
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHighSpeed
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_WRITE); // ifPromiscuousMode
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifConnectorPresent
columns[c++] =
new MOMutableColumn(c, SMIConstants.SYNTAX_OCTET_STRING, // ifAlias
MOAccessImpl.ACCESS_READ_WRITE, null);
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_TIMETICKS,
MOAccessImpl.ACCESS_READ_ONLY); // ifCounterDiscontinuityTime
DefaultMOTable ifXTable =
new DefaultMOTable(new OID("1.3.6.1.2.1.31.1.1.1"), indexDef, columns);
MOMutableTableModel model = (MOMutableTableModel) ifXTable.getModel();
Variable[] rowValues1 = new Variable[] {
new OctetString("Ethernet-0"),
new Integer32(1),
new Integer32(2),
new Integer32(3),
new Integer32(4),
new Integer32(5),
new Integer32(6),
new Integer32(7),
new Integer32(8),
new Integer32(9),
new Integer32(10),
new Integer32(11),
new Integer32(12),
new Integer32(13),
new Integer32(14),
new Integer32(15),
new Integer32(16),
new OctetString("My eth"),
new TimeTicks(1000)
};
Variable[] rowValues2 = new Variable[] {
new OctetString("Loopback"),
new Integer32(21),
new Integer32(22),
new Integer32(23),
new Integer32(24),
new Integer32(25),
new Integer32(26),
new Integer32(27),
new Integer32(28),
new Integer32(29),
new Integer32(30),
new Integer32(31),
new Integer32(32),
new Integer32(33),
new Integer32(34),
new Integer32(35),
new Integer32(36),
new OctetString("My loop"),
new TimeTicks(2000)
};
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
ifXTable.setVolatile(true);
return ifXTable;
}
private static DefaultMOTable createStaticIfTable() {
MOTableSubIndex[] subIndexes =
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
MOColumn[] columns = new MOColumn[8];
int c = 0;
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifIndex
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
MOAccessImpl.ACCESS_READ_ONLY); // ifDescr
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifType
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
MOAccessImpl.ACCESS_READ_ONLY); // ifPhysAddress
columns[c++] =
new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, // ifAdminStatus
MOAccessImpl.ACCESS_READ_WRITE, null);
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifOperStatus
UsmUser user = new UsmUser(new OctetString(securityName), authenticationProtocol, new OctetString(authenticationPassphrase), privacyProtocol, new OctetString(privacyPassphrase));
DefaultMOTable ifTable =
new DefaultMOTable(new OID("1.3.6.1.2.1.2.2.1"), indexDef, columns);
MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
Variable[] rowValues1 = new Variable[] {
new Integer32(1),
new OctetString("eth0"),
new Integer32(6),
new Integer32(1500),
new Gauge32(100000000),
new OctetString("00:00:00:00:01"),
new Integer32(1),
new Integer32(1)
};
Variable[] rowValues2 = new Variable[] {
new Integer32(2),
new OctetString("loopback"),
new Integer32(24),
new Integer32(1500),
new Gauge32(10000000),
new OctetString("00:00:00:00:02"),
new Integer32(1),
new Integer32(1)
};
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
ifTable.setVolatile(true);
return ifTable;
}
public void initV3(UsmUser user, String securityName) {
// snmp.getUSM().addUser(user);
private static DefaultMOTable createStaticSnmp4sTable() {
MOTableSubIndex[] subIndexes =
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
MOColumn[] columns = new MOColumn[8];
int c = 0;
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_NULL, MOAccessImpl.ACCESS_READ_ONLY); // testNull
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // testBoolean
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifType
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY); //ifPhysAddress
columns[c++] = new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_WRITE,
null);
// ifAdminStatus
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY);
// ifOperStatus
DefaultMOTable ifTable =
new DefaultMOTable(new OID("1.3.6.1.4.1.50000.1.1"), indexDef, columns);
MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
Variable[] rowValues1 = new Variable[] {
new Integer32(1),
new OctetString("eth0"),
new Integer32(6),
new Integer32(1500),
new Gauge32(100000000),
new OctetString("00:00:00:00:01"),
new Integer32(1),
new Integer32(1)
};
Variable[] rowValues2 = new Variable[] {
new Integer32(2),
new OctetString("loopback"),
new Integer32(24),
new Integer32(1500),
new Gauge32(10000000),
new OctetString("00:00:00:00:02"),
new Integer32(1),
new Integer32(1)
};
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
ifTable.setVolatile(true);
return ifTable;
}
protected void initTransportMappings() throws IOException {
transportMappings = new TransportMapping[2];
Address addr = GenericAddress.parse(address);
TransportMapping tm =
TransportMappings.getInstance().createTransportMapping(addr);
transportMappings[0] = tm;
transportMappings[1] = new DefaultTcpTransportMapping(new TcpAddress(address));
}
public void start(String ip, String port) throws IOException {
address = ip + "/" + port;
//BasicConfigurator.configure();
init();
addShutdownHook();
// loadConfig(ImportModes.REPLACE_CREATE);
getServer().addContext(new OctetString("public"));
finishInit();
run();
sendColdStartNotification();
}
protected void unregisterManagedObjects() {
// here we should unregister those objects previously registered...
}
protected void addCommunities(SnmpCommunityMIB communityMIB) {
Variable[] com2sec = new Variable[] {
new OctetString("public"), // community name
new OctetString("cpublic"), // security name
getAgent().getContextEngineID(), // local engine ID
new OctetString("public"), // default context name
new OctetString(), // transport tag
new Integer32(StorageType.nonVolatile), // storage type
new Integer32(RowStatus.active) // row status
};
MOTableRow row =
communityMIB.getSnmpCommunityEntry().createRow(
new OctetString("public2public").toSubIndex(true), com2sec);
communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row);
// snmpCommunityMIB.setSourceAddressFiltering(true);
}
protected void registerSnmpMIBs() {
heartbeatMIB = new Snmp4jHeartbeatMib(super.getNotificationOriginator(),
new OctetString(),
super.snmpv2MIB.getSysUpTime());
agentppSimulationMIB = new AgentppSimulationMib();
super.registerSnmpMIBs();
}
UserTarget userTarget = new UserTarget();
userTarget.setSecurityName(new OctetString(securityName));
userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
protected void initMessageDispatcher() {
this.dispatcher = new MessageDispatcherImpl();
this.mpv3 = new MPv3(this.agent.getContextEngineID().getValue());
this.usm = new USM(SecurityProtocols.getInstance(), this.agent.getContextEngineID(), this.updateEngineBoots());
SecurityModels.getInstance().addSecurityModel(this.usm);
SecurityProtocols.getInstance().addDefaultProtocols();
this.dispatcher.addMessageProcessingModel(new MPv1());
this.dispatcher.addMessageProcessingModel(new MPv2c());
this.dispatcher.addMessageProcessingModel(this.mpv3);
this.initSnmpSession();
}
}
}

2
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTest.java → common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java

@ -19,7 +19,7 @@ import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
public class SnmpTest {
public class SnmpTestV2 {
public static void main(String[] args) throws IOException {
SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");

46
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV3.java

@ -0,0 +1,46 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.agent.CommandProcessor;
import org.snmp4j.mp.MPv3;
import org.snmp4j.smi.OctetString;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
public class SnmpTestV3 {
public static void main(String[] args) throws IOException {
SnmpDeviceSimulatorV3 device = new SnmpDeviceSimulatorV3(new CommandProcessor(new OctetString(MPv3.createLocalEngineID())) {
@Override
public void processPdu(CommandResponderEvent event) {
System.out.println("event: " + event);
}
});
device.start("0.0.0.0", "1610");
device.setUpMappings(Map.of(
".1.3.6.1.2.1.1.1.50", "12",
".1.3.6.1.2.1.2.1.52", "56",
".1.3.6.1.2.1.3.1.54", "yes",
".1.3.6.1.2.1.7.1.58", ""
));
new Scanner(System.in).nextLine();
}
}

10
common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json

@ -38,16 +38,6 @@
"dataType": "STRING"
}
]
},
{
"spec": "TELEMETRY_TRAPS_RECEIVING",
"mappings": [
{
"oid": ".1.3.6.1.2.8.7.1.56",
"key": "temperature_trap",
"dataType": "LONG"
}
]
}
]
}

13
common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json

@ -0,0 +1,13 @@
{
"address": "192.168.3.23",
"port": 1610,
"protocolVersion": "V3",
"username": "tb-user",
"engineId": "qwertyuioa",
"securityName": "tb-user",
"authenticationProtocol": "SHA_512",
"authenticationPassphrase": "sdfghjkloifgh",
"privacyProtocol": "DES",
"privacyPassphrase": "rtytguijokod"
}

2
common/transport/snmp/src/test/resources/snmp-device-transport-config.json

@ -1,6 +1,6 @@
{
"address": "127.0.0.1",
"port": 1610,
"password": "public",
"community": "public",
"protocolVersion": "V2C"
}

11
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java

@ -558,6 +558,14 @@ public class JsonConverter {
}
}
public static JsonElement parse(String json) {
return JSON_PARSER.parse(json);
}
public static String toJson(JsonElement element) {
return GSON.toJson(element);
}
public static void setTypeCastEnabled(boolean enabled) {
isTypeCastEnabled = enabled;
}
@ -599,8 +607,7 @@ public class JsonConverter {
.build();
}
private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String
provisionKey, String provisionSecret) {
private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) {
return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder()
.setProvisionDeviceKey(provisionKey)
.setProvisionDeviceSecret(provisionSecret)

Loading…
Cancel
Save