GraphLab: Distributed Graph-Parallel API  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
net_util.cpp
1 /**
2  * Copyright (c) 2009 Carnegie Mellon University.
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an "AS
13  * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language
15  * governing permissions and limitations under the License.
16  *
17  * For more about this software visit:
18  *
19  * http://www.graphlab.ml.cmu.edu
20  *
21  */
22 
23 
24 #include <cstring>
25 #include <cstdlib>
26 #include <iostream>
27 #include <sstream>
28 #include <sys/types.h>
29 #include <ifaddrs.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <graphlab/logger/assertions.hpp>
33 #include <graphlab/util/net_util.hpp>
34 namespace graphlab {
35 
36 
37 bool str_to_ip(const char* c, uint32_t& out) {
38  if (c == NULL) return false;
39  else return inet_pton(AF_INET, c, &out) > 0;
40 }
41 
42 bool ip_to_str(uint32_t ip, std::string& out) {
43  char ipstring[INET_ADDRSTRLEN] = {0};
44  const char* ret = inet_ntop(AF_INET, &ip, ipstring, INET_ADDRSTRLEN);
45  if (ret == NULL) return false;
46  out = std::string(ipstring);
47  return true;
48 }
49 
50 
51 
52 std::string get_local_ip_as_str(bool print) {
53  uint32_t ip = get_local_ip(print);
54  if (ip == 0) return "127.0.0.1";
55  else {
56  std::string out;
57  bool ip_conversion_success = ip_to_str(ip, out);
58  ASSERT_TRUE(ip_conversion_success);
59  return out;
60  }
61 }
62 
63 uint32_t get_local_ip(bool print) {
64  // see if GRAPHLAB_SUBNET environment variable is set
65  char* c_subnet_id = getenv("GRAPHLAB_SUBNET_ID");
66  char* c_subnet_mask = getenv("GRAPHLAB_SUBNET_MASK");
67  uint32_t subnet_id = 0;
68  uint32_t subnet_mask = 0;
69  std::string str_subnet_id, str_subnet_mask;
70  // try to convert to a valid address when possible
71  if (c_subnet_id != NULL) {
72  if (!str_to_ip(c_subnet_id, subnet_id)) {
73  std::cout << "Unable to convert GRAPHLAB_SUBNET_ID to a valid address. Cannot continue\n";
74  exit(1);
75  }
76  }
77  if (c_subnet_mask != NULL) {
78  if (!str_to_ip(c_subnet_mask, subnet_mask)) {
79  std::cout << "Unable to convert GRAPHLAB_SUBNET_MASK to a valid address. Cannot continue\n";
80  exit(1);
81  }
82  }
83 
84  // error checking.
85  // By the end of this block, we should either have both subnet_id and subnet_mask filled
86  // to reasonable values, or are dead.
87 
88  if (c_subnet_id == NULL && c_subnet_mask != NULL) {
89  // If subnet mask specified but not subnet ID, we cannot continue.
90  std::cout << "GRAPHLAB_SUBNET_MASK specified, but GRAPHLAB_SUBNET_ID not specified.\n";
91  std::cout << "We cannot continue\n";
92  exit(1);
93  }
94  if (c_subnet_id != NULL && c_subnet_mask == NULL) {
95  if (print) {
96  std::cout << "GRAPHLAB_SUBNET_ID specified, but GRAPHLAB_SUBNET_MASK not specified.\n";
97  std::cout << "We will try to guess a subnet mask\n";
98  }
99  // if subnet id specified, but not subnet mask. We can try to guess a mask
100  // by finding the first "on" bit in the subnet id, and matching everything
101  // to the left of it.
102  // easiest way to do that is to left extend the subnet_id
103  subnet_mask = subnet_id;
104  subnet_mask = ntohl(subnet_mask);
105  subnet_mask = subnet_mask | (subnet_mask << 1);
106  subnet_mask = subnet_mask | (subnet_mask << 2);
107  subnet_mask = subnet_mask | (subnet_mask << 4);
108  subnet_mask = subnet_mask | (subnet_mask << 8);
109  subnet_mask = subnet_mask | (subnet_mask << 16);
110  subnet_mask = htonl(subnet_mask);
111  }
112  else {
113  if (print) {
114  std::cout << "GRAPHLAB_SUBNET_ID/GRAPHLAB_SUBNET_MASK environment variables not defined.\n";
115  std::cout << "Using default values\n";
116  }
117  }
118  ip_to_str(subnet_id, str_subnet_id);
119  ip_to_str(subnet_mask, str_subnet_mask);
120 
121  // make sure this is a valid subnet address.
122  if (print) {
123  std::cout << "Subnet ID: " << str_subnet_id << "\n";
124  std::cout << "Subnet Mask: " << str_subnet_mask << "\n";
125  std::cout << "Will find first IPv4 non-loopback address matching the subnet" << std::endl;
126  }
127  uint32_t ip(0);
128  // code adapted from
129  struct ifaddrs * ifAddrStruct = NULL;
130  getifaddrs(&ifAddrStruct);
131  struct ifaddrs * firstifaddr = ifAddrStruct;
132  ASSERT_NE(ifAddrStruct, NULL);
133  bool success = false;
134  while (ifAddrStruct != NULL) {
135  if (ifAddrStruct->ifa_addr != NULL &&
136  ifAddrStruct->ifa_addr->sa_family == AF_INET) {
137  char* tmpAddrPtr = NULL;
138  // check it is IP4 and not lo0.
139  tmpAddrPtr = (char*)&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
140  ASSERT_NE(tmpAddrPtr, NULL);
141  if (tmpAddrPtr[0] != 127) {
142  memcpy(&ip, tmpAddrPtr, 4);
143  // test if it matches the subnet
144  if ((ip & subnet_mask) == subnet_id) {
145  success = true;
146  break;
147  }
148  }
149  //break;
150  }
151  ifAddrStruct=ifAddrStruct->ifa_next;
152  }
153  freeifaddrs(firstifaddr);
154  if (!success) {
155  // if subnet addresses specified, and if we cannot find a valid network. Fail."
156  if (c_subnet_id!= NULL) {
157  std::cout << "Unable to find a network matching the requested subnet\n";
158  exit(1);
159  } else {
160  std::cout << "Unable to find any valid IPv4 address. Defaulting to loopback\n";
161  }
162  }
163  return ip;
164 }
165 
166 std::pair<size_t, int> get_free_tcp_port() {
167  int sock = socket(AF_INET, SOCK_STREAM, 0);
168  // uninteresting boiler plate. Set the port number and socket type
169  sockaddr_in my_addr;
170  my_addr.sin_family = AF_INET;
171  my_addr.sin_port = 0; // port 0.
172  my_addr.sin_addr.s_addr = INADDR_ANY;
173  memset(&(my_addr.sin_zero), '\0', 8);
174  if (bind(sock, (sockaddr*)&my_addr, sizeof(my_addr)) < 0){
175  logger(LOG_FATAL, "Failed to bind to a port 0! Unable to acquire a free TCP port!");
176  }
177  // get the sock information
178  socklen_t slen;
179  sockaddr addr;
180  slen = sizeof(sockaddr);
181  if (getsockname(sock, &addr, &slen) < 0) {
182  logger(LOG_FATAL, "Failed to get port information about bound socket");
183  }
184  size_t freeport = ntohs(((sockaddr_in*)(&addr))->sin_port);
185  std::pair<size_t, int> ret(freeport, sock);
186  return ret;
187 }
188 
189 } // namespace graphlab
190