#include <grpcpp/grpcpp.h>
#include <grpc/impl/codegen/grpc_types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
// 1. The Mutator: Focused ONLY on local IP binding
class LocalBindMutator : public grpc_socket_mutator {
public:
LocalBindMutator(const std::string& ip) : ip_(ip) {
// Initialize the base C-style vtable
grpc_socket_mutator_init(this, &vtable_);
}
static bool Mutate(int fd, grpc_socket_mutator* mutator) {
auto* self = static_cast<LocalBindMutator*>(mutator);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = 0; // Let OS pick the source port
inet_pton(AF_INET, self->ip_.c_str(), &addr.sin_addr);
// Bind the socket to the specific local IP
return bind(fd, (reinterpret_cast<const sockaddr*>(&addr)), sizeof(addr)) == 0;
}
static int Compare(grpc_socket_mutator* a, grpc_socket_mutator* b) {
return (a < b) ? -1 : ((a > b) ? 1 : 0);
}
static void Destroy(grpc_socket_mutator* mutator) {
delete static_cast<LocalBindMutator*>(mutator);
}
private:
std::string ip_;
static constexpr grpc_socket_mutator_vtable vtable_ = { Mutate, Compare, Destroy };
};
// Define the static vtable outside the class
constexpr grpc_socket_mutator_vtable LocalBindMutator::vtable_;
// 2. The Wrapper API
std::shared_ptr<grpc::Channel> CreateVendorChannel(
const std::string& target,
const std::string& local_ip,
int dscp_value)
{
grpc::ChannelArguments args;
// A. Handle DSCP via Native gRPC Argument
if (dscp_value >= 0) {
args.SetInt(GRPC_ARG_DSCP, dscp_value);
}
// B. Handle Local IP Binding via Mutator
if (!local_ip.empty()) {
auto* mutator = new LocalBindMutator(local_ip);
args.SetPointer(GRPC_ARG_SOCKET_MUTATOR, mutator);
}
return grpc::CreateCustomChannel(target,
grpc::InsecureChannelCredentials(),
args);
}