Envoy 源码分析--network

目录Envoy源码分析--networkaddressInstanceDNScidrsocketOptionSocketListenSocketConnectionSocketTransportSocketlistenconnectionConnectionImplClientConnectionImplEnvoy源码分析--network申明:本文的Envoy源码分析基于Envoy1.10.0。...

Envoy 源码分析--network

目录

  • Envoy 源码分析--network
    • address
      • Instance
      • DNS
      • cidr
    • socket
      • Option
      • Socket
      • ListenSocket
      • ConnectionSocket
      • TransportSocket
    • listen
    • connection
      • ConnectionImpl
      • ClientConnectionImpl

Envoy 源码分析--network

申明:本文的 Envoy 源码分析基于 Envoy1.10.0。

Envoy 的服务是通用服务,因此它需要支持 TCPUDP,同时还需支持 IPV4IPV6 两种网络协议,所以网络模块有点复杂。本次分析的网络模块是底层的模块,没有一整个服务的启动流程,有的地方可能还串不起来。现在先来看下UML类图:

类图看上去略显复杂,主要分为4块:addressocketlistenconnection

  • address 是地址相关的,主要包括 IPV4IPV6PIPEDNScidr
  • socketsocket 相关的操作,主要包括 ListenSocketConnectionSocketTransportSocket 以及 option
  • listen 是网络监听操作,包括 TCP 监听和 UDP 监听。
  • connection 是连接相关操作。关于 L3/4 过滤的这次暂时不分析,后续再讲。

address

InstanceBase 继承自 Instance 是所有地址类型的基类。Ipv4InstanceIpv6InstancePipeInstance 三个地址类都是继承 InstanceBaseDNS解析类使用 c-ares 库,DnsResolverImpl 只是对 c-ares 的进一步封装。 CidrRange 是对 cidr 操作相关。

系统操作返回的值和错误信息封装成一个公用的结构体。具体如下:

template <typename T> struct SysCallResult {  //系统返回值  T rc_;  //系统返回的错误信息  int errno_;};

Instance

Ipv4InstanceIpv6InstancePipeInstance 三个地址类都是继承 InstanceBase。它们的实现基本都差不多,socket()bind()connect() 这三个基础操作都属于它们的成员。现在我们主要来看下 Ipv4Instance 几个主要的操作(其它两个类类似就不再分析)。

Ipv4Instance 的类里有个私有结构体 IpHelper 。这结构体封装着 IPV4 地址的具体内容,比如端口,版本等

struct IpHelper : public Ip {  const std::string& addressAsString() const override { return friendly_address_; }  bool isAnyAddress() const override { return ipv4_.address_.sin_addr.s_addr == INADDR_ANY; }  bool isUnicastAddress() const override {  return !isAnyAddress() && (ipv4_.address_.sin_addr.s_addr != INADDR_BROADCAST) && // inlined IN_MULTICAST() to avoid byte swapping !((ipv4_.address_.sin_addr.s_addr & htonl(0xf0000000)) == htonl(0xe0000000));  }  const Ipv4* ipv4() const override { return &ipv4_; }  const Ipv6* ipv6() const override { return nullptr; }  uint32_t port() const override { return ntohs(ipv4_.address_.sin_port); }  IpVersion version() const override { return IpVersion::v4; }  Ipv4Helper ipv4_;  std::string friendly_address_;};

bind()socket()connect() 基本都是直接调的底层函数。

Api::SysCallIntResult Ipv6Instance::bind(int fd) const {  const int rc = ::bind(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv6_.address_),sizeof(ip_.ipv6_.address_));  return {rc, errno};}Api::SysCallIntResult Ipv6Instance::connect(int fd) const {  const int rc = ::connect(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv6_.address_),sizeof(ip_.ipv6_.address_));  return {rc, errno};}

DNS

DNS 使用 c-ares 作为底层库。 c-ares 是个 c 实现的异步 DNS 解析库,很多知名软件(curl,Nodejs,gevent 等)都使用了该库。

c-ares 在构造函数内初始化库,初始化上下文,然后设置 DNS 服务器。

DnsResolverImpl::DnsResolverImpl( Event::Dispatcher& dispatcher, const std::vector<Network::Address::InstanceConstSharedPtr>& resolvers)  : dispatcher_(dispatcher),timer_(dispatcher.createTimer([this] { onEventCallback(ARES_SOCKET_BAD, 0); })) {  //初始化库  ares_library_init(ARES_LIB_INIT_ALL);  ares_options options;  //初始化上下文  initializeChannel(&options, 0);  ... ...  const std::string resolvers_csv = StringUtil::join(resolver_addrs, “,“);  //设置 DNS 服务器  int result = ares_set_servers_ports_csv(channel_, resolvers_csv.c_str());}

使用时直接 resolve() 结果返回在 callback 里。

ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name,  DnsLookupFamily dns_lookup_family, ResolveCb callback) {  ... ...  if (dns_lookup_family == DnsLookupFamily::V4Only) { pending_resolution->getHostByName(AF_INET);  } else { pending_resolution->getHostByName(AF_INET6);  }  ... ...}void DnsResolverImpl::PendingResolution::getHostByName(int family) {  ares_gethostbyname(channel_, dns_name_.c_str(), family,[](void* arg, int status, int timeouts, hostent* hostent) {  static_cast<PendingResolution*>(arg)->onAresHostCallback(status, timeouts, hostent);},this);}void DnsResolverImpl::PendingResolution::onAresHostCallback(int status, int timeouts, hostent* hostent) {  ... ...  //解析内容加入address_list  std::list<Address::InstanceConstSharedPtr> address_list;  if (status == ARES_SUCCESS) { if (hostent->h_addrtype == AF_INET) {for (int i = 0; hostent->h_addr_list[i] != nullptr;i) {  ASSERT(hostent->h_length == sizeof(in_addr));  sockaddr_in address;  memset(&address, 0, sizeof(address));  address.sin_family = AF_INET;  address.sin_port = 0;  address.sin_addr = *reinterpret_cast<in_addr*>(hostent->h_addr_list[i]);  address_list.emplace_back(new Address::Ipv4Instance(&address));}... ...  }  if (completed_) { if (!cancelled_) {try {  //调用回调  callback_(std::move(address_list));} catch (const EnvoyException& e) {... ...}

cidr

cidr 的定义是形如 192.168.0.1/24 的 IP 段。想知道具体的定义和 IP 段 可看 cidr。

CidrRangecidr 拆分成两字段地址和长度。下面是判断地址是否属于这个 IP 段。

bool CidrRange::isInRange(const Instance& address) const {  ... ...  //长度为0,全匹配(length_初始值为-1)  if (length_ == 0) { return true;  }  switch (address.ip()->version()) {  case IpVersion::v4: if (ntohl(address.ip()->ipv4()->address()) >> (32 - length_) ==  ntohl(address_->ip()->ipv4()->address()) >> (32 - length_)) {return true; } break;  case IpVersion::v6: if ((Utility::Ip6ntohl(address_->ip()->ipv6()->address()) >> (128 - length_)) ==  (Utility::Ip6ntohl(address.ip()->ipv6()->address()) >> (128 - length_))) {return true; } break;  }  return false;}

socket

我们都知道,创建 TCP 服务时,监听的 fd 和连接的 fd 是不一样的,因此 socket 分为 ListenSocketConnectionSocketsocket 里有很多的配置(比如读超时,写超时等)都是调用setsockopt,所有需要一个 Option 来进行统一的封装。

Option

Option 是对 setsockopt 这个函数操作的封装。封装后再用智能指针的方式进行操作。

typedef std::shared_ptr<const Option> OptionConstSharedPtr;typedef std::vector<OptionConstSharedPtr> Options;typedef std::shared_ptr<Options> OptionsSharedPtr;
源文地址:https://www.guoxiongfei.cn/cntech/14852.html
0