繁体   English   中英

Java设置本地IP地址而不更改代码

[英]Java set local ip address without changing code

我有一台服务器,我们将向该服务器添加一个附加IP地址。 该服务器是在OpenVZ虚拟化平台上运行的CentOS VPS。

我想做的是在不影响现有Java程序的情况下引入新地址,所有现有程序都应继续使用主地址。

我意识到Socket()构造函数有一个变体,可以指定使用的IP地址-这不是一个理想的解决方案,因为它涉及到更改很多代码的事情-而且其中大部分是库代码,这简直是意料之外的多个IP,尤其是在执行客户端请求时。

对于新程序,我计划在Socket构造函数中显式指定辅助IP,但我希望现有程序仅使用“默认” IP而不修改它们。

有没有办法在我的JVM配置或OS配置中执行此操作。 如果有帮助,我也可以对主机节点具有管理员访问权限。

编辑

这是公认答案的实现。 它可以应用于大多数进程,而不仅限于Java程序。 该代码在C中,但使用它无需了解C。 说明在注释中。

此处说明

/*
Copyright (C) 2000  Daniel Ryde

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
*/

/*
LD_PRELOAD library to make bind and connect to use a virtual
IP address as localaddress. Specified via the enviroment
variable BIND_ADDR.

Compile on Linux with:
gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE

Example in bash to make inetd only listen to the localhost
lo interface, thus disabling remote connections and only
enable to/from localhost:

BIND_ADDR="127.0.0.1" LD_PRELOAD=./bind.so /sbin/inetd

Example in bash to use your virtual IP as your outgoing
sourceaddress for ircII:

BIND_ADDR="your-virt-ip" LD_PRELOAD=./bind.so ircII

Note that you have to set up your servers virtual IP first.


This program was made by Daniel Ryde
email: daniel@ryde.net
web:   http://www.ryde.net/

TODO: I would like to extend it to the accept calls too, like a
general tcp-wrapper. Also like an junkbuster for web-banners.
For libc5 you need to replace socklen_t with int.
*/



#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dlfcn.h>
#include <errno.h>

int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);

char *bind_addr_env;
unsigned long int bind_addr_saddr;
unsigned long int inaddr_any_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };

void _init (void)
{
    const char *err;

    real_bind = dlsym (RTLD_NEXT, "bind");
    if ((err = dlerror ()) != NULL) {
        fprintf (stderr, "dlsym (bind): %s\n", err);
    }

    real_connect = dlsym (RTLD_NEXT, "connect");
    if ((err = dlerror ()) != NULL) {
        fprintf (stderr, "dlsym (connect): %s\n", err);
    }

    inaddr_any_saddr = htonl (INADDR_ANY);
    if (bind_addr_env = getenv ("BIND_ADDR")) {
        bind_addr_saddr = inet_addr (bind_addr_env);
        local_sockaddr_in->sin_family = AF_INET;
        local_sockaddr_in->sin_addr.s_addr = bind_addr_saddr;
        local_sockaddr_in->sin_port = htons (0);
    }
}

int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
    static struct sockaddr_in *lsk_in;

    lsk_in = (struct sockaddr_in *)sk;
/*  printf("bind: %d %s:%d\n", fd, inet_ntoa (lsk_in->sin_addr.s_addr),
        ntohs (lsk_in->sin_port));*/
        if ((lsk_in->sin_family == AF_INET)
        && (lsk_in->sin_addr.s_addr == inaddr_any_saddr)
        && (bind_addr_env)) {
        lsk_in->sin_addr.s_addr = bind_addr_saddr;
    }
    return real_bind (fd, sk, sl);
}

int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
    static struct sockaddr_in *rsk_in;

    rsk_in = (struct sockaddr_in *)sk;
/*  printf("connect: %d %s:%d\n", fd, inet_ntoa (rsk_in->sin_addr.s_addr),
        ntohs (rsk_in->sin_port));*/
        if ((rsk_in->sin_family == AF_INET)
        && (bind_addr_env)) {
        real_bind (fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
    }
    return real_connect (fd, sk, sl);
}

编辑2如果该进程尝试将IPv4地址映射到IPv6地址,例如192.0.2.128变为:: ffff:192.0.2.128,则该解决方案将不起作用。 为了防止Java这样做,请使用以下VM参数

-Djava.net.preferIPv4Stack=true

围绕libc connect()进行包装,后者将bind()绑定到所需的地址(如果尚未绑定),并继续原始的connect()函数,并使用LD_PRELOAD将其附加到目标进程。 我从FreeBSD端口使用了一个名为“ libconnect”的文件。 遗憾的是,源代码已经消失了,但是对于有经验的Linux程序员而言,恢复源代码(并创建Linux端口)是一项非常容易的任务(1-2小时)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM