View Javadoc
1   package org.metricshub.ipmi.core.sm.states;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * IPMI Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 Verax Systems, MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import org.metricshub.ipmi.core.coding.Encoder;
26  import org.metricshub.ipmi.core.coding.commands.IpmiVersion;
27  import org.metricshub.ipmi.core.coding.commands.PrivilegeLevel;
28  import org.metricshub.ipmi.core.coding.commands.session.CloseSession;
29  import org.metricshub.ipmi.core.coding.commands.session.GetChannelAuthenticationCapabilities;
30  import org.metricshub.ipmi.core.coding.protocol.AuthenticationType;
31  import org.metricshub.ipmi.core.coding.protocol.Ipmiv20Message;
32  import org.metricshub.ipmi.core.coding.protocol.PayloadType;
33  import org.metricshub.ipmi.core.coding.protocol.decoder.ProtocolDecoder;
34  import org.metricshub.ipmi.core.coding.protocol.decoder.Protocolv20Decoder;
35  import org.metricshub.ipmi.core.coding.protocol.encoder.Protocolv20Encoder;
36  import org.metricshub.ipmi.core.coding.rmcp.RmcpMessage;
37  import org.metricshub.ipmi.core.coding.security.CipherSuite;
38  import org.metricshub.ipmi.core.common.TypeConverter;
39  import org.metricshub.ipmi.core.sm.StateMachine;
40  import org.metricshub.ipmi.core.sm.actions.ErrorAction;
41  import org.metricshub.ipmi.core.sm.actions.MessageAction;
42  import org.metricshub.ipmi.core.sm.events.Sendv20Message;
43  import org.metricshub.ipmi.core.sm.events.SessionUpkeep;
44  import org.metricshub.ipmi.core.sm.events.StateMachineEvent;
45  import org.metricshub.ipmi.core.sm.events.Timeout;
46  
47  /**
48   * {@link State} in which the session is valid and sending IPMI commands to the
49   * remote machine is enabled. 
50   * <ul>
51   * <li>Sends an IPMI v2.0 message on
52   * {@link Sendv20Message} <li>Sends {@link GetChannelAuthenticationCapabilities}
53   * message to keep the session form timing out on {@link SessionUpkeep}</li>
54   * <li> Transits to {@link Authcap} on {@link Timeout} <li>Sends {@link CloseSession}
55   * and transits to {@link Authcap} on {@link org.metricshub.ipmi.core.coding.commands.session.CloseSession}</li>
56   * </ul>
57   */
58  public class SessionValid extends State {
59  
60      private CipherSuite cipherSuite;
61  
62      private int sessionId;
63  
64      public CipherSuite getCipherSuite() {
65          return cipherSuite;
66      }
67  
68      /**
69       * Initiates the state.
70       *
71       * @param cipherSuite
72       *            - {@link CipherSuite} used during the session.
73       */
74      public SessionValid(CipherSuite cipherSuite, int sessionId) {
75          this.cipherSuite = cipherSuite;
76          this.sessionId = sessionId;
77      }
78  
79      @Override
80      public void doTransition(StateMachine stateMachine,
81              StateMachineEvent machineEvent) {
82          if (machineEvent instanceof Sendv20Message) {
83              Sendv20Message event = (Sendv20Message) machineEvent;
84              try {
85                  stateMachine.sendMessage(Encoder.encode(
86                          new Protocolv20Encoder(), event.getPayloadCoder(), event.getMessageSequenceNumber(),
87                          event.getSessionSequenceNumber(), event.getSessionId()));
88              } catch (Exception e) {
89                  stateMachine.doExternalAction(new ErrorAction(e));
90              }
91          } else if (machineEvent instanceof SessionUpkeep) {
92              SessionUpkeep event = (SessionUpkeep) machineEvent;
93              try {
94                  stateMachine.sendMessage(Encoder.encode(
95                          new Protocolv20Encoder(),
96                          new GetChannelAuthenticationCapabilities(
97                                  IpmiVersion.V20, IpmiVersion.V20, cipherSuite,
98                                  PrivilegeLevel.Callback, TypeConverter.intToByte(0xe)),
99                                  event.getMessageSequenceNumber(), event.getSessionSequenceNumber(), event.getSessionId()));
100             } catch (Exception e) {
101                 stateMachine.doExternalAction(new ErrorAction(e));
102             }
103         } else if (machineEvent instanceof Timeout) {
104             stateMachine.setCurrent(new Authcap());
105         } else if (machineEvent instanceof org.metricshub.ipmi.core.sm.events.CloseSession) {
106             org.metricshub.ipmi.core.sm.events.CloseSession event = (org.metricshub.ipmi.core.sm.events.CloseSession) machineEvent;
107 
108             try {
109                 stateMachine.setCurrent(new Authcap());
110                 stateMachine.sendMessage(Encoder.encode(
111                         new Protocolv20Encoder(),
112                         new CloseSession(IpmiVersion.V20, cipherSuite, AuthenticationType.RMCPPlus, event.getSessionId()),
113                                 event.getMessageSequenceNumber(), event.getSessionSequenceNumber(), event.getSessionId()));
114             } catch (Exception e) {
115                 stateMachine.setCurrent(this);
116                 stateMachine.doExternalAction(new ErrorAction(e));
117             }
118         } else {
119             stateMachine.doExternalAction(new ErrorAction(
120                     new IllegalArgumentException("Invalid transition")));
121         }
122 
123     }
124 
125     @Override
126     public void doAction(StateMachine stateMachine, RmcpMessage message) {
127         if (ProtocolDecoder.decodeAuthenticationType(message) != AuthenticationType.RMCPPlus) {
128             return; // this isn't IPMI v2.0 message so we ignore it
129         }
130         if (Protocolv20Decoder.decodeSessionID(message) == 0) {
131             return; // this is a sessionless message so we drop it
132         }
133         Protocolv20Decoder decoder = new Protocolv20Decoder(cipherSuite);
134         PayloadType payloadType = Protocolv20Decoder.decodePayloadType(message.getData()[1]);
135 
136         if (payloadType != PayloadType.Ipmi && payloadType != PayloadType.Sol) {
137             return;
138         }
139         if (Protocolv20Decoder.decodeSessionID(message) != sessionId) {
140             return; // this message belongs to other session so we ignore it
141         }
142         try {
143             Ipmiv20Message message20 = (Ipmiv20Message) decoder.decode(message);
144             if (message20.getSessionID() == sessionId) {
145                 stateMachine.doExternalAction(new MessageAction(message20));
146             }
147         } catch (Exception e) {
148             stateMachine.doExternalAction(new ErrorAction(e));
149         }
150     }
151 
152 }