summaryrefslogtreecommitdiff
path: root/src/components/transport_manager/include/transport_manager/tcp/platform_specific/linux/platform_specific_network_interface_listener_impl.h
blob: 4a78af4eb1de1da73faf30bd0794e5d98980a51a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#ifndef SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_PLATFORM_SPECIFIC_LINUX_PLATFORM_SPECIFIC_NETWORK_INTERFACE_LISTENER_H_
#define SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_PLATFORM_SPECIFIC_LINUX_PLATFORM_SPECIFIC_NETWORK_INTERFACE_LISTENER_H_

#include <map>
#include <string>
#include <vector>

#include <netinet/in.h>
#include "transport_manager/tcp/network_interface_listener.h"
#include "utils/macro.h"
#include "utils/threads/thread_delegate.h"

struct ifaddrmsg;

namespace transport_manager {
namespace transport_adapter {

class TcpClientListener;

/**
 * @brief Struct to keep network interface's status flags and IP addresses
 */
class InterfaceStatus {
 public:
  InterfaceStatus()
      : flags_(0)
      , has_ipv4_(false)
      , has_ipv6_(false)
      , ipv4_address_()
      , ipv6_address_() {}
  ~InterfaceStatus() {}

  bool IsAvailable() const;
  bool IsLoopback() const;
  const std::string& GetName() const {
    return name_;
  }
  // only for debugging output
  unsigned int GetFlags() const {
    return flags_;
  }

  bool HasIPAddress() const;
  const std::string GetIPv4Address() const;
  const std::string GetIPv6Address() const;

  void SetName(const std::string& name) {
    name_ = name;
  }

  void SetFlags(unsigned int flags) {
    flags_ = flags;
  }

  // specify NULL to remove existing address
  void SetIPv4Address(const struct in_addr* addr);
  void SetIPv6Address(const struct in6_addr* addr);

 private:
  std::string name_;
  unsigned int flags_;
  bool has_ipv4_;
  bool has_ipv6_;
  struct in_addr ipv4_address_;
  struct in6_addr ipv6_address_;
};

/**
 * @brief A map to store various status (IP addresses, status flags, etc.) of
 *        network interfaces. Keys of the map are index numbers of interfaces.
 */
typedef std::map<unsigned int, InterfaceStatus> InterfaceStatusTable;

/**
 * @brief Listener to detect various events on network interfaces
 */
class PlatformSpecificNetworkInterfaceListener
    : public NetworkInterfaceListener {
 public:
  /**
   * @brief Constructor
   *
   * @param tcp_client_listener  an instance of TcpClientListener which receives
   *                             status updates
   * @param designated_interface  if we want to listen only on a specific
   *                              network interface, specify its name
   */
  PlatformSpecificNetworkInterfaceListener(
      TcpClientListener* tcp_client_listener,
      const std::string designated_interface = "");

  /**
   * @brief Destructor
   */
  virtual ~PlatformSpecificNetworkInterfaceListener();

  /**
   * @brief Initialize this listener
   */
  bool Init() OVERRIDE;

  /**
   * @brief Deinitialize this listener
   */
  void Deinit() OVERRIDE;

  /**
   * @brief Start this listener
   */
  bool Start() OVERRIDE;

  /**
   * @brief Stop this listener
   */
  bool Stop() OVERRIDE;

#ifdef BUILD_TESTS
  void SetTesting(bool enabled) {
    testing_ = enabled;
  }

  int GetSocket() const {
    return socket_;
  }

  threads::Thread* GetThread() const {
    return thread_;
  }

  void OverwriteStatusTable(const InterfaceStatusTable dummy_table) {
    status_table_ = dummy_table;
  }

  void testCallNotifyIPAddresses() {
    NotifyIPAddresses();
  }

  const std::string& GetSelectedInterfaceName() const {
    return selected_interface_;
  }

  // for testing only: overwrites if_indextoname() by registering a dummy
  // index-to-name mapping table
  void SetDummyNameMap(const std::map<unsigned int, std::string>& m) {
    dummy_name_map_ = m;
  }
#endif  // BUILD_TESTS

 private:
  // Struct to hold an event on a network interface.
  // The event can be either an update on flags or an update on IP address.
  struct EventParam {
    unsigned int if_index;
    unsigned int flags;
    struct sockaddr_storage address;

    EventParam(int interface_index, unsigned int interface_flags = 0)
        : if_index(interface_index), flags(interface_flags), address() {}
  };

  // use with std::find_if() to search for an entry containing specified
  // interface name
  struct NamePredicate {
    NamePredicate(const std::string& name) : name_(name) {}
    bool operator()(
        const std::pair<unsigned int, InterfaceStatus>& entry) const {
      return entry.second.GetName() == name_;
    }
    const std::string& name_;
  };

  // parent class which we will notify the events to
  TcpClientListener* tcp_client_listener_;
  // if configured, NetworkInterfaceListener will always look into the IP
  // addresses of this interface
  const std::string designated_interface_;

  // a map to store status of each interface
  InterfaceStatusTable status_table_;
  // this is the name of the interface we are currently focusing on
  std::string selected_interface_;
  // previous IP addresses that we have notified
  std::string notified_ipv4_addr_;
  std::string notified_ipv6_addr_;

  int socket_;
  int pipe_fds_[2];
  threads::Thread* thread_;

#ifdef BUILD_TESTS
  bool testing_;
  std::map<unsigned int, std::string> dummy_name_map_;
#endif

  void Loop();
  bool StopLoop();

  // reset status_table_ by fetching current status of each interface
  bool InitializeStatus();
  // update status_table_ by applying the events
  bool UpdateStatus(uint16_t type, std::vector<EventParam>& params);
  // update notified_ipv4_addr_ and notified_ipv6_addr_ then notify the parent
  // class of the change if necessary
  void NotifyIPAddresses();
  // Select an appropriate network interface that we will get IP addresses. Also
  // update selected_interface_.
  const std::string SelectInterface();
  // convert ifaddrmsg to a list of EventParam structs
  std::vector<EventParam> ParseIFAddrMessage(struct ifaddrmsg* message,
                                             unsigned int size);
  // return network interface name of the specified index
  const std::string GetInterfaceName(unsigned int if_index) const;
  // for debugging
  void DumpTable() const;

  class ListenerThreadDelegate : public threads::ThreadDelegate {
   public:
    explicit ListenerThreadDelegate(
        PlatformSpecificNetworkInterfaceListener* parent);
    virtual void threadMain();
    void exitThreadMain();

   private:
    PlatformSpecificNetworkInterfaceListener* parent_;
  };

  DISALLOW_COPY_AND_ASSIGN(PlatformSpecificNetworkInterfaceListener);
};

}  // namespace transport_adapter
}  // namespace transport_manager

#endif  // SRC_COMPONENTS_TRANSPORT_MANAGER_INCLUDE_TRANSPORT_MANAGER_TCP_PLATFORM_SPECIFIC_LINUX_PLATFORM_SPECIFIC_NETWORK_INTERFACE_LISTENER_H_